diff --git a/renderdoc/Makefile b/renderdoc/Makefile index 573e9ec77..6838afa2c 100644 --- a/renderdoc/Makefile +++ b/renderdoc/Makefile @@ -75,6 +75,7 @@ data/glsl/mesh.geomo \ data/glsl/text.verto \ data/glsl/text.frago \ data/glsl/histogram.compo \ +data/glsl/arraymscopy.compo \ data/sourcecodepro.ttfo .PHONY: all diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 721ae0ce4..e87a54d40 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -166,6 +166,8 @@ enum SpecialFormat eSpecial_BC5, eSpecial_BC6, eSpecial_BC7, + eSpecial_ETC2, + eSpecial_EAC, eSpecial_R10G10B10A2, eSpecial_R11G11B10, eSpecial_B5G6R5, diff --git a/renderdoc/common/dds_readwrite.cpp b/renderdoc/common/dds_readwrite.cpp index fa86d2d87..922dc7ded 100644 --- a/renderdoc/common/dds_readwrite.cpp +++ b/renderdoc/common/dds_readwrite.cpp @@ -509,6 +509,8 @@ DXGI_FORMAT ResourceFormat2DXGIFormat(ResourceFormat format) case eSpecial_D32S8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; default: + case eSpecial_ETC2: + case eSpecial_EAC: case eSpecial_YUV: RDCERR("Unsupported writing format %u", format.specialFormat); return DXGI_FORMAT_UNKNOWN; @@ -680,6 +682,11 @@ bool write_dds_to_file(FILE *f, const dds_data &data) case eSpecial_BC6: case eSpecial_BC7: blockFormat = true; + break; + case eSpecial_ETC2: + case eSpecial_EAC: + RDCERR("Unsupported file format, ETC2/EAC"); + return false; default: break; } @@ -745,7 +752,7 @@ bool write_dds_to_file(FILE *f, const dds_data &data) bytesPerPixel = 5; break; case eSpecial_YUV: - RDCERR("Unsupported file save format"); + RDCERR("Unsupported file format"); return false; default: bytesPerPixel = data.format.compCount*data.format.compByteWidth; @@ -1014,7 +1021,7 @@ dds_data load_dds_from_file(FILE *f) bytesPerPixel = 5; break; case eSpecial_YUV: - RDCERR("Unsupported file save format"); + RDCERR("Unsupported file format, YUV"); return error; default: bytesPerPixel = ret.format.compCount*ret.format.compByteWidth; @@ -1034,6 +1041,11 @@ dds_data load_dds_from_file(FILE *f) case eSpecial_BC6: case eSpecial_BC7: blockFormat = true; + break; + case eSpecial_ETC2: + case eSpecial_EAC: + RDCERR("Unsupported file format, ETC2/EAC"); + return error; default: break; } diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index e4ba52471..463979b95 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -35,6 +35,7 @@ DECLARE_EMBED(blit_frag); DECLARE_EMBED(texdisplay_frag); DECLARE_EMBED(checkerboard_frag); DECLARE_EMBED(histogram_comp); +DECLARE_EMBED(arraymscopy_comp); DECLARE_EMBED(mesh_vert); DECLARE_EMBED(mesh_frag); DECLARE_EMBED(mesh_geom); diff --git a/renderdoc/data/glsl/arraymscopy.comp b/renderdoc/data/glsl/arraymscopy.comp new file mode 100644 index 000000000..10fceb5fc --- /dev/null +++ b/renderdoc/data/glsl/arraymscopy.comp @@ -0,0 +1,65 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2015 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#extension GL_ARB_compute_shader : require + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +uniform int numMultiSamples; + + + +layout(binding = 0) uniform usampler2DMSArray srcMS; +layout(binding = 0) writeonly uniform uimage2DArray dstArray; + +void MS2Array() +{ + uvec3 id = gl_GlobalInvocationID; + + int slice = int(id.z / numMultiSamples); + int sampleIdx = int(id.z % numMultiSamples); + + uvec4 data = texelFetch(srcMS, ivec3(int(id.x), int(id.y), slice), sampleIdx); + + imageStore(dstArray, ivec3(int(id.x), int(id.y), int(id.z)), data); +} + + + + +layout(binding = 0) uniform usampler2DArray srcArray; +layout(binding = 0) writeonly uniform uimage2DMSArray dstMS; + +void Array2MS() +{ + uvec3 id = gl_GlobalInvocationID; + + int slice = int(id.z / numMultiSamples); + int sampleIdx = int(id.z % numMultiSamples); + + uvec4 data = texelFetch(srcArray, ivec3(int(id.x), int(id.y), int(id.z)), 0); + + imageStore(dstMS, ivec3(int(id.x), int(id.y), slice), sampleIdx, data); +} \ No newline at end of file diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index 28f7e8099..f78a00075 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -123,6 +123,7 @@ RESOURCE_texsample_h TYPE_EMBED "glsl/texsample.h" RESOURCE_histogram_comp TYPE_EMBED "glsl/histogram.comp" RESOURCE_mesh_frag TYPE_EMBED "glsl/mesh.frag" RESOURCE_mesh_geom TYPE_EMBED "glsl/mesh.geom" +RESOURCE_arraymscopy_comp TYPE_EMBED "glsl/arraymscopy.comp" RESOURCE_sourcecodepro_ttf TYPE_EMBED "sourcecodepro.ttf" diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 054458cab..51ac4656f 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -25,6 +25,7 @@ #define RESOURCE_histogram_comp 212 #define RESOURCE_mesh_frag 213 #define RESOURCE_mesh_geom 214 +#define RESOURCE_arraymscopy_comp 215 #define RESOURCE_sourcecodepro_ttf 301 diff --git a/renderdoc/driver/gl/gl_debug.cpp b/renderdoc/driver/gl/gl_debug.cpp index 001682bbb..de451e070 100644 --- a/renderdoc/driver/gl/gl_debug.cpp +++ b/renderdoc/driver/gl/gl_debug.cpp @@ -344,6 +344,18 @@ void GLReplay::InitDebugData() gl.glNamedBufferStorageEXT(DebugData.minmaxResult, sizeof(Vec4f)*2, NULL, GL_MAP_READ_BIT); gl.glNamedBufferStorageEXT(DebugData.histogramBuf, sizeof(uint32_t)*HGRAM_NUM_BUCKETS, NULL, GL_MAP_READ_BIT); } + + { + string glsl = "#version 420 core\n\n#define MS2Array main\n\n"; + glsl += GetEmbeddedResource(arraymscopy_comp); + + DebugData.MS2Array = CreateCShaderProgram(glsl.c_str()); + + glsl = "#version 420 core\n\n#define Array2MS main\n\n"; + glsl += GetEmbeddedResource(arraymscopy_comp); + + DebugData.Array2MS = CreateCShaderProgram(glsl.c_str()); + } gl.glGenVertexArrays(1, &DebugData.meshVAO); gl.glBindVertexArray(DebugData.meshVAO); @@ -479,6 +491,9 @@ void GLReplay::DeleteDebugData() gl.glDeleteProgram(DebugData.minmaxResultProgram[i]); } } + + gl.glDeleteProgram(DebugData.Array2MS); + gl.glDeleteProgram(DebugData.MS2Array); gl.glDeleteBuffers(1, &DebugData.minmaxTileResult); gl.glDeleteBuffers(1, &DebugData.minmaxResult); @@ -853,13 +868,67 @@ void GLReplay::PickPixel(ResourceId texture, uint32_t x, uint32_t y, uint32_t sl texDisplay.offx = -float(x); texDisplay.offy = -float(y); - RenderTexture(texDisplay); + RenderTextureInternal(texDisplay, false); } gl.glReadPixels(0, 0, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)pixel); } +void GLReplay::CopyTex2DMSToArray(GLuint destArray, GLuint srcMS, GLint width, GLint height, GLint arraySize, GLint samples, GLenum intFormat) +{ + WrappedOpenGL &gl = *m_pDriver; + + GLRenderState rs(&gl.GetHookset(), NULL, READING); + rs.FetchState(m_pDriver->GetCtx(), m_pDriver); + + GLenum viewClass; + gl.glGetInternalformativ(eGL_TEXTURE_2D_ARRAY, intFormat, eGL_VIEW_COMPATIBILITY_CLASS, sizeof(GLenum), (GLint *)&viewClass); + + GLenum fmt = eGL_R32UI; + if(viewClass == eGL_VIEW_CLASS_8_BITS) fmt = eGL_R8UI; + else if(viewClass == eGL_VIEW_CLASS_16_BITS) fmt = eGL_R16UI; + else if(viewClass == eGL_VIEW_CLASS_24_BITS) fmt = eGL_RGB8UI; + else if(viewClass == eGL_VIEW_CLASS_32_BITS) fmt = eGL_RGBA8UI; + else if(viewClass == eGL_VIEW_CLASS_48_BITS) fmt = eGL_RGB16UI; + else if(viewClass == eGL_VIEW_CLASS_64_BITS) fmt = eGL_RG32UI; + else if(viewClass == eGL_VIEW_CLASS_96_BITS) fmt = eGL_RGB32UI; + else if(viewClass == eGL_VIEW_CLASS_128_BITS) fmt = eGL_RGBA32UI; + + GLuint texs[2]; + gl.glGenTextures(2, texs); + gl.glTextureView(texs[0], eGL_TEXTURE_2D_ARRAY, destArray, fmt, 0, 1, 0, arraySize*samples); + gl.glTextureView(texs[1], eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, srcMS, fmt, 0, 1, 0, arraySize); + + gl.glBindImageTexture(0, texs[0], 0, GL_TRUE, 0, eGL_WRITE_ONLY, fmt); + gl.glActiveTexture(eGL_TEXTURE0); + gl.glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, texs[1]); + gl.glBindSampler(0, DebugData.pointNoMipSampler); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_BASE_LEVEL, 0); + gl.glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE_ARRAY, eGL_TEXTURE_MAX_LEVEL, 1); + + gl.glUseProgram(DebugData.MS2Array); + + GLint loc = gl.glGetUniformLocation(DebugData.MS2Array, "numMultiSamples"); + gl.glUniform1i(loc, samples); + + gl.glDispatchCompute((GLuint)width, (GLuint)height, GLuint(arraySize*samples)); + gl.glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + + gl.glDeleteTextures(2, texs); + + rs.ApplyState(m_pDriver->GetCtx(), m_pDriver); +} + bool GLReplay::RenderTexture(TextureDisplay cfg) +{ + return RenderTextureInternal(cfg, true); +} + +bool GLReplay::RenderTextureInternal(TextureDisplay cfg, bool blendAlpha) { WrappedOpenGL &gl = *m_pDriver; @@ -1119,7 +1188,7 @@ bool GLReplay::RenderTexture(TextureDisplay cfg) gl.glUnmapBuffer(eGL_UNIFORM_BUFFER); - if(cfg.rawoutput) + if(cfg.rawoutput || !blendAlpha) { gl.glDisable(eGL_BLEND); } diff --git a/renderdoc/driver/gl/gl_renderstate.cpp b/renderdoc/driver/gl/gl_renderstate.cpp index 896da533e..b31dcc561 100644 --- a/renderdoc/driver/gl/gl_renderstate.cpp +++ b/renderdoc/driver/gl/gl_renderstate.cpp @@ -44,6 +44,25 @@ void PixelUnpackState::Fetch(const GLHookSet *funcs, bool compressed) } } +void PixelUnpackState::Apply(const GLHookSet *funcs, bool compressed) +{ + funcs->glPixelStorei(eGL_UNPACK_SWAP_BYTES, swapBytes); + funcs->glPixelStorei(eGL_UNPACK_ROW_LENGTH, rowlength); + funcs->glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, imageheight); + funcs->glPixelStorei(eGL_UNPACK_SKIP_PIXELS, skipPixels); + funcs->glPixelStorei(eGL_UNPACK_SKIP_ROWS, skipRows); + funcs->glPixelStorei(eGL_UNPACK_SKIP_IMAGES, skipImages); + funcs->glPixelStorei(eGL_UNPACK_ALIGNMENT, alignment); + + if(compressed) + { + funcs->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_WIDTH, compressedBlockWidth); + funcs->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_HEIGHT, compressedBlockHeight); + funcs->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_DEPTH, compressedBlockDepth); + funcs->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_SIZE, compressedBlockSize); + } +} + bool PixelUnpackState::FastPath(GLsizei width, GLsizei height, GLsizei depth, GLenum dataformat, GLenum basetype) { if(swapBytes) @@ -845,18 +864,8 @@ void GLRenderState::ApplyState(void *ctx, WrappedOpenGL *gl) m_Real->glFrontFace(FrontFace); m_Real->glCullFace(CullFace); - - m_Real->glPixelStorei(eGL_UNPACK_SWAP_BYTES, Unpack.swapBytes); - m_Real->glPixelStorei(eGL_UNPACK_ROW_LENGTH, Unpack.rowlength); - m_Real->glPixelStorei(eGL_UNPACK_IMAGE_HEIGHT, Unpack.imageheight); - m_Real->glPixelStorei(eGL_UNPACK_SKIP_PIXELS, Unpack.skipPixels); - m_Real->glPixelStorei(eGL_UNPACK_SKIP_ROWS, Unpack.skipRows); - m_Real->glPixelStorei(eGL_UNPACK_SKIP_IMAGES, Unpack.skipImages); - m_Real->glPixelStorei(eGL_UNPACK_ALIGNMENT, Unpack.alignment); - m_Real->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_WIDTH, Unpack.compressedBlockWidth); - m_Real->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_HEIGHT, Unpack.compressedBlockHeight); - m_Real->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_DEPTH, Unpack.compressedBlockDepth); - m_Real->glPixelStorei(eGL_UNPACK_COMPRESSED_BLOCK_SIZE, Unpack.compressedBlockSize); + + Unpack.Apply(m_Real, true); } void GLRenderState::Clear() diff --git a/renderdoc/driver/gl/gl_renderstate.h b/renderdoc/driver/gl/gl_renderstate.h index f70cafab2..8e0babe72 100644 --- a/renderdoc/driver/gl/gl_renderstate.h +++ b/renderdoc/driver/gl/gl_renderstate.h @@ -42,6 +42,7 @@ struct PixelUnpackState int32_t compressedBlockSize; void Fetch(const GLHookSet *funcs, bool compressed); + void Apply(const GLHookSet *funcs, bool compressed); bool FastPath(GLsizei width, GLsizei height, GLsizei depth, GLenum dataformat=eGL_NONE, GLenum basetype=eGL_NONE); byte *Unpack(byte *pixels, GLsizei width, GLsizei height, GLsizei depth, GLenum dataformat, GLenum basetype); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index ca025e291..7124d5860 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2168,6 +2168,313 @@ void GLReplay::FillCBufferVariables(ResourceId shader, uint32_t cbufSlot, vector FillCBufferVariables(gl, curProg, cblock.bufferBacked ? true : false, "", cblock.variables, outvars, data); } +byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, bool resolve, bool forceRGBA8unorm, float blackPoint, float whitePoint, size_t &dataSize) +{ + WrappedOpenGL &gl = *m_pDriver; + + auto &texDetails = m_pDriver->m_Textures[tex]; + + byte *ret = NULL; + + GLuint tempTex = 0; + + GLenum texType = texDetails.curType; + GLuint texname = texDetails.resource.name; + GLenum intFormat = texDetails.internalFormat; + GLsizei width = RDCMAX(1, texDetails.width>>mip); + GLsizei height = RDCMAX(1, texDetails.height>>mip); + GLsizei depth = RDCMAX(1, texDetails.depth>>mip); + GLsizei arraysize = 1; + GLint samples = texDetails.samples; + + if(texType == eGL_TEXTURE_2D_ARRAY || + texType == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY || + texType == eGL_TEXTURE_1D_ARRAY || + texType == eGL_TEXTURE_CUBE_MAP || + texType == eGL_TEXTURE_CUBE_MAP_ARRAY) + { + // array size doesn't get mip'd down + depth = texDetails.depth; + arraysize = texDetails.depth; + } + + if(forceRGBA8unorm && intFormat != eGL_RGBA8 && intFormat != eGL_SRGB8_ALPHA8) + { + MakeCurrentReplayContext(m_DebugCtx); + + GLenum finalFormat = IsSRGBFormat(intFormat) ? eGL_SRGB8_ALPHA8 : eGL_RGBA8; + GLenum newtarget = (texType == eGL_TEXTURE_3D ? eGL_TEXTURE_3D : eGL_TEXTURE_2D); + + // create temporary texture of width/height in RGBA8 format to render to + gl.glGenTextures(1, &tempTex); + gl.glBindTexture(newtarget, tempTex); + if(newtarget == eGL_TEXTURE_3D) + gl.glTexStorage3D(newtarget, 1, finalFormat, width, height, depth); + else + gl.glTexStorage2D(newtarget, 1, finalFormat, width, height); + + // create temp framebuffer + GLuint fbo = 0; + gl.glGenFramebuffers(1, &fbo); + gl.glBindFramebuffer(eGL_FRAMEBUFFER, fbo); + + gl.glTexParameteri(newtarget, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); + gl.glTexParameteri(newtarget, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); + gl.glTexParameteri(newtarget, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); + gl.glTexParameteri(newtarget, eGL_TEXTURE_WRAP_T, eGL_CLAMP_TO_EDGE); + gl.glTexParameteri(newtarget, eGL_TEXTURE_WRAP_R, eGL_CLAMP_TO_EDGE); + if(newtarget == eGL_TEXTURE_3D) + gl.glFramebufferTexture3D(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, eGL_TEXTURE_3D, tempTex, 0, 0); + else + gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, tempTex, 0); + + float col[] = { 0.3f, 0.6f, 0.9f, 1.0f }; + gl.glClearBufferfv(eGL_COLOR, 0, col); + + // render to the temp texture to do the downcast + float w = DebugData.outWidth; + float h = DebugData.outHeight; + + DebugData.outWidth = float(width); DebugData.outHeight = float(height); + + for(GLsizei d=0; d < (newtarget == eGL_TEXTURE_3D ? depth : 1); d++) + { + TextureDisplay texDisplay; + + texDisplay.Red = texDisplay.Green = texDisplay.Blue = texDisplay.Alpha = true; + texDisplay.HDRMul = -1.0f; + texDisplay.linearDisplayAsGamma = false; + texDisplay.overlay = eTexOverlay_None; + texDisplay.FlipY = false; + texDisplay.mip = mip; + texDisplay.sampleIdx = ~0U; + texDisplay.CustomShader = ResourceId(); + texDisplay.sliceFace = arrayIdx; + texDisplay.rangemin = blackPoint; + texDisplay.rangemax = whitePoint; + texDisplay.scale = 1.0f; + texDisplay.texid = tex; + texDisplay.rawoutput = false; + texDisplay.offx = 0; + texDisplay.offy = 0; + + if(newtarget == eGL_TEXTURE_3D) + { + gl.glFramebufferTexture3D(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, eGL_TEXTURE_3D, tempTex, 0, (GLint)d); + texDisplay.sliceFace = (uint32_t)d; + } + + gl.glViewport(0, 0, width, height); + + RenderTextureInternal(texDisplay, false); + } + + // rewrite the variables to temporary texture + texType = newtarget; + texname = tempTex; + intFormat = finalFormat; + if(newtarget == eGL_TEXTURE_2D) depth = 1; + arraysize = 1; + samples = 1; + + gl.glDeleteFramebuffers(1, &fbo); + } + else if(resolve && samples > 1) + { + MakeCurrentReplayContext(m_DebugCtx); + + GLuint curDrawFBO = 0; + GLuint curReadFBO = 0; + gl.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curDrawFBO); + gl.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, (GLint*)&curReadFBO); + + // create temporary texture of width/height in same format to render to + gl.glGenTextures(1, &tempTex); + gl.glBindTexture(eGL_TEXTURE_2D, tempTex); + gl.glTexStorage2D(eGL_TEXTURE_2D, 1, intFormat, width, height); + + // create temp framebuffers + GLuint fbos[2] = { 0 }; + gl.glGenFramebuffers(2, fbos); + + gl.glBindFramebuffer(eGL_FRAMEBUFFER, fbos[0]); + gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, tempTex, 0); + + gl.glBindFramebuffer(eGL_FRAMEBUFFER, fbos[1]); + if(texType == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY) + gl.glFramebufferTextureLayer(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, texname, 0, arrayIdx); + else + gl.glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, texname, 0); + + // do default resolve (framebuffer blit) + gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, fbos[0]); + gl.glBindFramebuffer(eGL_READ_FRAMEBUFFER, fbos[1]); + + float col[] = { 0.3f, 0.4f, 0.5f, 1.0f }; + gl.glClearBufferfv(eGL_COLOR, 0, col); + + gl.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, eGL_NEAREST); + + // rewrite the variables to temporary texture + texType = eGL_TEXTURE_2D; + texname = tempTex; + depth = 1; + arraysize = 1; + samples = 1; + + gl.glDeleteFramebuffers(2, fbos); + + gl.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, curDrawFBO); + gl.glBindFramebuffer(eGL_READ_FRAMEBUFFER, curReadFBO); + } + else if(samples > 1) + { + MakeCurrentReplayContext(m_DebugCtx); + + // create temporary texture array of width/height in same format to render to, + // with the same number of array slices as multi samples. + gl.glGenTextures(1, &tempTex); + gl.glBindTexture(eGL_TEXTURE_2D_ARRAY, tempTex); + gl.glTexStorage3D(eGL_TEXTURE_2D_ARRAY, 1, intFormat, width, height, arraysize*samples); + + // copy multisampled texture to an array + CopyTex2DMSToArray(tempTex, texname, width, height, arraysize, samples, intFormat); + + // rewrite the variables to temporary texture + texType = eGL_TEXTURE_2D_ARRAY; + texname = tempTex; + depth = 1; + depth = samples; + arraysize = samples; + samples = 1; + } + + // fetch and return data now + { + PixelUnpackState unpack; + + unpack.Fetch(&gl.GetHookset(), true); + + PixelUnpackState identity = {0}; + identity.alignment = 1; + + identity.Apply(&gl.GetHookset(), true); + + GLenum binding = TextureBinding(texType); + + GLuint prevtex = 0; + gl.glGetIntegerv(binding, (GLint *)&prevtex); + + gl.glBindTexture(texType, texname); + + GLenum target = texType; + if(texType == eGL_TEXTURE_CUBE_MAP) + { + GLenum targets[] = { + eGL_TEXTURE_CUBE_MAP_POSITIVE_X, + eGL_TEXTURE_CUBE_MAP_NEGATIVE_X, + eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, + eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, + eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + }; + + RDCASSERT(arrayIdx < ARRAY_COUNT(targets)); + target = targets[arrayIdx]; + } + + if(IsCompressedFormat(intFormat)) + { + GLuint compSize; + gl.glGetTexLevelParameteriv(target, mip, eGL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint *)&compSize); + + dataSize = compSize; + + ret = new byte[dataSize]; + + gl.glGetCompressedTexImage(target, mip, ret); + } + else + { + GLenum fmt = GetBaseFormat(intFormat); + GLenum type = GetDataType(intFormat); + + dataSize = GetByteSize(width, height, depth, fmt, type); + ret = new byte[dataSize]; + + m_pDriver->glGetTexImage(target, (GLint)mip, fmt, type, ret); + + // need to vertically flip the image now to get conventional row ordering + // we either do this when copying out the slice of interest, or just + // on its own + size_t rowSize = GetByteSize(width, 1, 1, fmt, type); + byte *src, *dst; + + // for arrays just extract the slice we're interested in. + if(texType == eGL_TEXTURE_2D_ARRAY || + texType == eGL_TEXTURE_1D_ARRAY || + texType == eGL_TEXTURE_CUBE_MAP_ARRAY) + { + dataSize = GetByteSize(width, height, 1, fmt, type); + byte *slice = new byte[dataSize]; + + // src points to the last row in the array slice image + src = (ret + dataSize*arrayIdx) + (height-1)*rowSize; + dst = slice; + + // we do memcpy + vertical flip + //memcpy(slice, ret + dataSize*arrayIdx, dataSize); + + for(GLsizei i=0; i < height; i++) + { + memcpy(dst, src, rowSize); + + dst += rowSize; + src -= rowSize; + } + + delete[] ret; + + ret = slice; + } + else + { + byte *row = new byte[rowSize]; + + size_t sliceSize = GetByteSize(width, height, 1, fmt, type); + + // invert all slices in a 3D texture + for(GLsizei d=0; d < depth; d++) + { + dst = ret + d*sliceSize; + src = dst + (height-1)*rowSize; + + for(GLsizei i=0; i < height>>1; i++) + { + memcpy(row, src, rowSize); + memcpy(src, dst, rowSize); + memcpy(dst, row, rowSize); + + dst += rowSize; + src -= rowSize; + } + } + + delete[] row; + } + } + + unpack.Apply(&gl.GetHookset(), true); + + gl.glBindTexture(texType, prevtex); + } + + if(tempTex) + gl.glDeleteTextures(1, &tempTex); + + return ret; +} + #pragma endregion vector GLReplay::GetUsage(ResourceId id) @@ -2191,12 +2498,6 @@ void GLReplay::FreeCustomShader(ResourceId id) RDCUNIMPLEMENTED("FreeCustomShader"); } -byte *GLReplay::GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, bool resolve, bool forceRGBA8unorm, float blackPoint, float whitePoint, size_t &dataSize) -{ - RDCUNIMPLEMENTED("GetTextureData"); - return NULL; -} - void GLReplay::ReplaceResource(ResourceId from, ResourceId to) { RDCUNIMPLEMENTED("ReplaceResource"); diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 304a51109..83c5f84eb 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -152,6 +152,7 @@ class GLReplay : public IReplayDriver void FreeCustomShader(ResourceId id); bool RenderTexture(TextureDisplay cfg); + bool RenderTextureInternal(TextureDisplay cfg, bool blendAlpha); void RenderCheckerboard(Vec3f light, Vec3f dark); @@ -194,6 +195,9 @@ class GLReplay : public IReplayDriver const vector &data); void GetMapping(WrappedOpenGL &gl, GLuint curProg, int shadIdx, ShaderReflection *refl, ShaderBindpointMapping &mapping); + + void CopyArrayToTex2DMS(GLuint destMS, GLuint srcArray, GLint width, GLint height, GLint arraySize, GLint samples, GLenum intFormat); + void CopyTex2DMSToArray(GLuint destArray, GLuint srcMS, GLint width, GLint height, GLint arraySize, GLint samples, GLenum intFormat); struct OutputWindow : public GLWindowingData { @@ -250,6 +254,8 @@ class GLReplay : public IReplayDriver GLuint texDisplayProg[3]; // float/uint/sint + GLuint MS2Array, Array2MS; + GLuint pointSampler; GLuint pointNoMipSampler; GLuint linearSampler; diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 89e71749d..9196f8a32 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -366,6 +366,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 3b9b6dc13..c4bef7d36 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -607,6 +607,9 @@ Resources\glsl + + Resources\glsl + diff --git a/renderdoc/replay/replay_renderer.cpp b/renderdoc/replay/replay_renderer.cpp index feb9b807e..69723b8ee 100644 --- a/renderdoc/replay/replay_renderer.cpp +++ b/renderdoc/replay/replay_renderer.cpp @@ -601,6 +601,11 @@ bool ReplayRenderer::SaveTexture(const TextureSave &saveData, const char *path) // force downcast to be able to do grid mappings if(sd.slice.cubeCruciform || sd.slice.slicesAsGrid) downcast = true; + + // we don't support any file formats that handle these block compression formats + if(td.format.specialFormat == eSpecial_ETC2 || + td.format.specialFormat == eSpecial_EAC) + downcast = true; // for DDS don't downcast, for non-HDR always downcast if we're not already RGBA8 unorm // for HDR&EXR we can convert from most regular types as well as 10.10.10.2 and 11.11.10 diff --git a/renderdocui/Interop/Enums.cs b/renderdocui/Interop/Enums.cs index ff0febdbc..02bd6e8ac 100644 --- a/renderdocui/Interop/Enums.cs +++ b/renderdocui/Interop/Enums.cs @@ -168,6 +168,8 @@ namespace renderdoc BC5, BC6, BC7, + ETC2, + EAC, R10G10B10A2, R11G11B10, B5G6R5,