From c0b931eb15d1f5fcc29cfc068bea0ee554108deb Mon Sep 17 00:00:00 2001 From: Bruce He Date: Wed, 9 Nov 2022 22:18:05 -0500 Subject: [PATCH] Improved fragment id shader code - Deleted WriteClearValues and replace with QueryPostModValues - Add support for earlier GLSL versions and fix logic for creating primitive id program - Changed to only run per fragment queries on events with multiple fragments --- renderdoc/CMakeLists.txt | 1 + renderdoc/data/embedded_files.h | 1 + renderdoc/data/glsl/pixelhistory_primid.frag | 2 +- .../data/glsl/pixelhistory_primid_legacy.frag | 32 ++ renderdoc/data/renderdoc.rc | 1 + renderdoc/data/resource.h | 1 + renderdoc/driver/gl/gl_pixelhistory.cpp | 497 ++++++++++++------ renderdoc/renderdoc.vcxproj | 1 + renderdoc/renderdoc.vcxproj.filters | 3 + 9 files changed, 388 insertions(+), 151 deletions(-) create mode 100644 renderdoc/data/glsl/pixelhistory_primid_legacy.frag diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index 830183fa4..4f5913a5e 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -433,6 +433,7 @@ set(data data/glsl/pixelhistory_mscopy.comp data/glsl/pixelhistory_mscopy_depth.comp data/glsl/pixelhistory_primid.frag + data/glsl/pixelhistory_primid_legacy.frag data/glsl/shaderdebug_sample.vert data/glsl/texdisplay.frag data/glsl/texremap.frag diff --git a/renderdoc/data/embedded_files.h b/renderdoc/data/embedded_files.h index 9787913f9..086b1277e 100644 --- a/renderdoc/data/embedded_files.h +++ b/renderdoc/data/embedded_files.h @@ -66,6 +66,7 @@ DECLARE_EMBED(glsl_gles_texsample_h); DECLARE_EMBED(glsl_pixelhistory_mscopy_comp); DECLARE_EMBED(glsl_pixelhistory_mscopy_depth_comp); DECLARE_EMBED(glsl_pixelhistory_primid_frag); +DECLARE_EMBED(glsl_pixelhistory_primid_legacy_frag); DECLARE_EMBED(glsl_shaderdebug_sample_vert); DECLARE_EMBED(glsl_texremap_frag); DECLARE_EMBED(glsl_discard_frag); diff --git a/renderdoc/data/glsl/pixelhistory_primid.frag b/renderdoc/data/glsl/pixelhistory_primid.frag index a7768e1f1..d1c6a0d1b 100644 --- a/renderdoc/data/glsl/pixelhistory_primid.frag +++ b/renderdoc/data/glsl/pixelhistory_primid.frag @@ -28,5 +28,5 @@ IO_LOCATION(0) out vec4 color_out; void main(void) { - color_out = intBitsToFloat(gl_PrimitiveID).xxxx; + color_out = vec4(intBitsToFloat(gl_PrimitiveID)); } diff --git a/renderdoc/data/glsl/pixelhistory_primid_legacy.frag b/renderdoc/data/glsl/pixelhistory_primid_legacy.frag new file mode 100644 index 000000000..5a4e054cc --- /dev/null +++ b/renderdoc/data/glsl/pixelhistory_primid_legacy.frag @@ -0,0 +1,32 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2020-2022 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. + ******************************************************************************/ + +#include "glsl_globals.h" + +IO_LOCATION(0) out vec4 color_out; + +void main(void) +{ + color_out = vec4(float(gl_PrimitiveID)); +} diff --git a/renderdoc/data/renderdoc.rc b/renderdoc/data/renderdoc.rc index d2cf025fc..d57cb445b 100644 --- a/renderdoc/data/renderdoc.rc +++ b/renderdoc/data/renderdoc.rc @@ -156,6 +156,7 @@ RESOURCE_glsl_histogram_comp TYPE_EMBED "glsl/histogram.comp" RESOURCE_glsl_pixelhistory_mscopy_comp TYPE_EMBED "glsl/pixelhistory_mscopy.comp" RESOURCE_glsl_pixelhistory_mscopy_depth_comp TYPE_EMBED "glsl/pixelhistory_mscopy_depth.comp" RESOURCE_glsl_pixelhistory_primid_frag TYPE_EMBED "glsl/pixelhistory_primid.frag" +RESOURCE_glsl_pixelhistory_primid_legacy_frag TYPE_EMBED "glsl/pixelhistory_primid_legacy.frag" RESOURCE_glsl_glsl_ubos_h TYPE_EMBED "glsl/glsl_ubos.h" RESOURCE_glsl_gl_texsample_h TYPE_EMBED "glsl/gl_texsample.h" RESOURCE_glsl_vk_texsample_h TYPE_EMBED "glsl/vk_texsample.h" diff --git a/renderdoc/data/resource.h b/renderdoc/data/resource.h index 0ef0d8c96..8894abc42 100644 --- a/renderdoc/data/resource.h +++ b/renderdoc/data/resource.h @@ -65,6 +65,7 @@ #define RESOURCE_glsl_vk_depthms2buffer_comp 448 #define RESOURCE_glsl_vk_buffer2ms_comp 449 #define RESOURCE_glsl_vk_depthbuf2ms_frag 450 +#define RESOURCE_glsl_pixelhistory_primid_legacy_frag 451 // Next default values for new objects // diff --git a/renderdoc/driver/gl/gl_pixelhistory.cpp b/renderdoc/driver/gl/gl_pixelhistory.cpp index b5b73fbd9..0af6c12ad 100644 --- a/renderdoc/driver/gl/gl_pixelhistory.cpp +++ b/renderdoc/driver/gl/gl_pixelhistory.cpp @@ -45,6 +45,7 @@ struct GLPixelHistoryResources GLuint dsImage; GLuint frameBuffer; GLuint primitiveIdFragmentShader; + GLuint primitiveIdFragmentShaderSPIRV; std::unordered_map programs; }; @@ -64,6 +65,193 @@ enum class PerFragmentQueryType PrimitiveId }; +GLuint CreatePrimitiveIdProgram(GLuint Program, GLuint Pipeline, GLuint fragShader, + GLuint fragShaderSPIRV, WrappedOpenGL *driver, GLReplay *replay) +{ + WrappedOpenGL &drv = *driver; + + ContextPair &ctx = drv.GetCtx(); + + GLuint primitiveIdProgram = drv.glCreateProgram(); + + // these are the shaders to attach, and the programs to copy details from + GLuint shaders[4] = {0}; + GLuint programs[4] = {0}; + + // temporary programs created as needed if the original program was created with + // glCreateShaderProgramv and we don't have a shader to attach + GLuint tmpShaders[4] = {0}; + + // the reflection for the vertex shader, used to copy vertex bindings + ShaderReflection *vsRefl = NULL; + + bool HasSPIRVShaders = false; + bool HasGLSLShaders = false; + + if(Program == 0) + { + if(Pipeline == 0) + { + return false; + } + else + { + ResourceId id = driver->GetResourceManager()->GetResID(ProgramPipeRes(ctx, Pipeline)); + const WrappedOpenGL::PipelineData &pipeDetails = driver->m_Pipelines[id]; + + // fetch the corresponding shaders and programs for each stage + for(size_t i = 0; i < 4; i++) + { + if(pipeDetails.stageShaders[i] != ResourceId()) + { + const WrappedOpenGL::ShaderData &shadDetails = + driver->m_Shaders[pipeDetails.stageShaders[i]]; + + if(shadDetails.reflection->encoding == ShaderEncoding::SPIRV) + HasSPIRVShaders = true; + else + HasGLSLShaders = true; + + programs[i] = + driver->GetResourceManager()->GetCurrentResource(pipeDetails.stagePrograms[i]).name; + shaders[i] = + driver->GetResourceManager()->GetCurrentResource(pipeDetails.stageShaders[i]).name; + + if(pipeDetails.stagePrograms[i] == pipeDetails.stageShaders[i]) + { + const WrappedOpenGL::ProgramData &progDetails = + driver->m_Programs[pipeDetails.stagePrograms[i]]; + + if(progDetails.shaderProgramUnlinkable) + { + rdcarray sources; + sources.reserve(shadDetails.sources.size()); + + for(const rdcstr &s : shadDetails.sources) + sources.push_back(s.c_str()); + + shaders[i] = tmpShaders[i] = drv.glCreateShader(ShaderEnum(i)); + drv.glShaderSource(tmpShaders[i], (GLsizei)sources.size(), sources.data(), NULL); + drv.glCompileShader(tmpShaders[i]); + + GLint status = 0; + drv.glGetShaderiv(tmpShaders[i], eGL_COMPILE_STATUS, &status); + + if(status == 0) + { + char buffer[1024] = {}; + drv.glGetShaderInfoLog(tmpShaders[i], 1024, NULL, buffer); + RDCERR("Trying to create primitive id program, couldn't compile shader:\n%s", buffer); + } + } + } + + if(i == 0) + vsRefl = replay->GetShader(ResourceId(), pipeDetails.stageShaders[i], ShaderEntryPoint()); + } + } + } + } + else + { + const WrappedOpenGL::ProgramData &progDetails = + driver->m_Programs[driver->GetResourceManager()->GetResID(ProgramRes(ctx, Program))]; + + // fetch any and all non-fragment shader shaders + for(size_t i = 0; i < 4; i++) + { + if(progDetails.stageShaders[i] != ResourceId()) + { + programs[i] = Program; + shaders[i] = + driver->GetResourceManager()->GetCurrentResource(progDetails.stageShaders[i]).name; + + const WrappedOpenGL::ShaderData &shadDetails = driver->m_Shaders[progDetails.stageShaders[i]]; + + if(shadDetails.reflection->encoding == ShaderEncoding::SPIRV) + HasSPIRVShaders = true; + else + HasGLSLShaders = true; + + if(i == 0) + vsRefl = replay->GetShader(ResourceId(), progDetails.stageShaders[0], ShaderEntryPoint()); + } + } + } + + if(HasGLSLShaders && HasSPIRVShaders) + RDCERR("Unsupported - mixed GLSL and SPIR-V shaders in pipeline"); + + // attach the shaders + for(size_t i = 0; i < 4; i++) + if(shaders[i]) + drv.glAttachShader(primitiveIdProgram, shaders[i]); + + if(HasSPIRVShaders) + { + RDCASSERT(fragShaderSPIRV); + drv.glAttachShader(primitiveIdProgram, fragShaderSPIRV); + } + else + { + drv.glAttachShader(primitiveIdProgram, fragShader); + } + + // copy the vertex attribs over from the source program + if(vsRefl && programs[0] && !HasSPIRVShaders) + CopyProgramAttribBindings(programs[0], primitiveIdProgram, vsRefl); + + // link the overlay program + drv.glLinkProgram(primitiveIdProgram); + + // detach the shaders + for(size_t i = 0; i < 4; i++) + if(shaders[i]) + drv.glDetachShader(primitiveIdProgram, shaders[i]); + + if(HasSPIRVShaders) + drv.glDetachShader(primitiveIdProgram, fragShaderSPIRV); + else + drv.glDetachShader(primitiveIdProgram, fragShader); + + // delete any temporaries + for(size_t i = 0; i < 4; i++) + if(tmpShaders[i]) + drv.glDeleteShader(tmpShaders[i]); + + // check that the link succeeded + char buffer[1024] = {}; + GLint status = 0; + drv.glGetProgramiv(primitiveIdProgram, eGL_LINK_STATUS, &status); + if(status == 0) + { + drv.glGetProgramInfoLog(primitiveIdProgram, 1024, NULL, buffer); + RDCERR("Error linking primitive id program: %s", buffer); + return false; + } + + // copy the uniform values over from the source program. This is redundant but harmless if the + // same program is bound to multiple stages. It's just inefficient + { + PerStageReflections dstStages; + driver->FillReflectionArray(ProgramRes(ctx, primitiveIdProgram), dstStages); + + for(size_t i = 0; i < 4; i++) + { + if(programs[i]) + { + PerStageReflections stages; + driver->FillReflectionArray(ProgramRes(ctx, programs[i]), stages); + + CopyProgramUniforms(stages, programs[i], dstStages, primitiveIdProgram); + } + } + } + + // return HasSPIRVShaders; + return primitiveIdProgram; +} + GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay, GLPixelHistoryResources &resources, GLuint currentProgram) { @@ -73,69 +261,19 @@ GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay, return programIterator->second; } - GLuint newProgram; + GLRenderState rs; + rs.FetchState(driver); - GLint numAttachedShaders; - driver->glGetProgramiv(currentProgram, eGL_ATTACHED_SHADERS, &numAttachedShaders); - - newProgram = driver->glCreateProgram(); - - GLuint *attachedShaders = new GLuint[numAttachedShaders]; - - driver->glGetAttachedShaders(currentProgram, numAttachedShaders, &numAttachedShaders, - attachedShaders); - - ShaderReflection *vsRefl = NULL; - for(int i = 0; i < numAttachedShaders; ++i) - { - GLint shaderType; - driver->glGetShaderiv(attachedShaders[i], eGL_SHADER_TYPE, &shaderType); - - if(shaderType != eGL_FRAGMENT_SHADER) - { - driver->glAttachShader(newProgram, attachedShaders[i]); - } - - if(shaderType == eGL_VERTEX_SHADER) - { - vsRefl = replay->GetShader(ResourceId(), driver->GetResourceManager()->GetResID( - ShaderRes(driver->GetCtx(), attachedShaders[i])), - ShaderEntryPoint()); - } - } - delete[] attachedShaders; - - driver->glAttachShader(newProgram, resources.primitiveIdFragmentShader); - if(vsRefl) - { - CopyProgramAttribBindings(currentProgram, newProgram, vsRefl); - } - driver->glLinkProgram(newProgram); - - char buffer[1024] = {}; - GLint status = 0; - driver->glGetProgramiv(newProgram, eGL_LINK_STATUS, &status); - if(status == 0) - { - GL.glGetProgramInfoLog(newProgram, 1024, NULL, buffer); - RDCERR("Shader error: %s", buffer); - } - - resources.programs[currentProgram] = newProgram; - - PerStageReflections dstStages; - driver->FillReflectionArray(ProgramRes(driver->GetCtx(), newProgram), dstStages); - - PerStageReflections stages; - driver->FillReflectionArray(ProgramRes(driver->GetCtx(), newProgram), stages); - CopyProgramUniforms(stages, currentProgram, dstStages, newProgram); + GLuint newProgram = CreatePrimitiveIdProgram( + rs.Program.name, rs.Pipeline.name, resources.primitiveIdFragmentShader, + resources.primitiveIdFragmentShaderSPIRV, driver, replay); return newProgram; } bool PixelHistorySetupResources(WrappedOpenGL *driver, GLPixelHistoryResources &resources, const TextureDescription &desc, const Subresource &sub, - uint32_t numEvents) + uint32_t numEvents, GLuint glslVersion) { // Allocate a framebuffer that will render to the textures driver->glGenFramebuffers(1, &resources.frameBuffer); @@ -156,15 +294,24 @@ bool PixelHistorySetupResources(WrappedOpenGL *driver, GLPixelHistoryResources & desc.msSamp, 1); driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, resources.dsImage, 0); - // The pixel history primitive ID Fragment Shader requires at least version 420. - // This is because it requires scalars to be swizzled something enabled - // in version 420. We also require intBitsToFloat which is introduced in - // version 330. - int GLSLVersion = 420; - - rdcstr fs = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), - ShaderType::GLSL, GLSLVersion); - resources.primitiveIdFragmentShader = CreateShader(eGL_FRAGMENT_SHADER, fs); + // If the GLSL version is greater than or equal to 330, we can use IntBitsToFloat, otherwise we + // need to write the float value directly. + rdcstr glslSource; + if(glslVersion >= 330) + { + glslSource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), + ShaderType::GLSL, glslVersion); + } + else + { + glslSource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_legacy_frag), + ShaderType::GLSL, glslVersion); + } + // SPIR-V shaders are always generated as desktop GL 430, for ease + rdcstr spirvSource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), + ShaderType::GLSPIRV, 430); + resources.primitiveIdFragmentShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, spirvSource); + resources.primitiveIdFragmentShader = CreateShader(eGL_FRAGMENT_SHADER, glslSource); return true; } @@ -175,6 +322,7 @@ bool PixelHistoryDestroyResources(WrappedOpenGL *driver, const GLPixelHistoryRes driver->glDeleteTextures(1, &resources.dsImage); driver->glDeleteFramebuffers(1, &resources.frameBuffer); driver->glDeleteShader(resources.primitiveIdFragmentShader); + driver->glDeleteShader(resources.primitiveIdFragmentShaderSPIRV); for(const std::pair &resourceProgram : resources.programs) { @@ -195,7 +343,7 @@ rdcarray QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryR driver->ReplayLog(0, events[0].eventId, eReplay_WithoutDraw); // execute the occlusion queries - for(size_t i = 0; i < events.size(); ++i) + for(size_t i = 0; i < events.size(); i++) { if(!(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage))) { @@ -220,7 +368,7 @@ rdcarray QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryR } } // read back the occlusion queries and generate the list of potentially modifying events - for(size_t i = 0; i < events.size(); ++i) + for(size_t i = 0; i < events.size(); i++) { if(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage)) { @@ -251,9 +399,48 @@ rdcarray QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryR return modEvents; } +void QueryPostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &resources, + const rdcarray &modEvents, int x, int y, + rdcarray &history) +{ + driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); + driver->glClear(eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT | eGL_STENCIL_BUFFER_BIT); + driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw); + + for(size_t i = 0; i < modEvents.size(); i++) + { + GLint savedReadFramebuffer, savedDrawFramebuffer; + driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); + driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); + // bind our own framebuffer to save the pixel values + driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); + driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_Full); + + // read the post mod pixel value into the history event + ModificationValue modValue; + PixelValue pixelValue; + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)pixelValue.floatValue.data()); + driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth); + driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_INT, (void *)&modValue.stencil); + modValue.col = pixelValue; + + history[i].postMod = modValue; + + // restore the capture's framebuffer + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); + + if(i < modEvents.size() - 1) + { + driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw); + } + } +} + std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, GLPixelHistoryResources &resources, - const rdcarray &modEvents, int x, + const rdcarray &modEvents, + rdcarray &history, int x, int y) { driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); @@ -264,30 +451,45 @@ std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, for(size_t i = 0; i < modEvents.size(); ++i) { - if(!isDirectWrite(modEvents[i].usage) && modEvents[i].usage != ResourceUsage::Clear) - { - GLint savedReadFramebuffer, savedDrawFramebuffer; - driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); - driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); - // bind our own framebuffer to save the pixel values - driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); - driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR); - driver->glStencilMask(0xff); - driver->glClearStencil(0); - driver->glClear(eGL_STENCIL_BUFFER_BIT); - driver->glEnable(eGL_STENCIL_TEST); - driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_Full); + GLint savedReadFramebuffer, savedDrawFramebuffer; + driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); + driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); + // bind our own framebuffer to save the pixel values + driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); + driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR); + driver->glStencilMask(0xff); // default for 1 byte + driver->glStencilFunc(eGL_ALWAYS, 0, 0xff); + driver->glClearStencil(0); + driver->glClear(eGL_STENCIL_BUFFER_BIT); + driver->glEnable(eGL_STENCIL_TEST); + // depth test enable + driver->glEnable(eGL_DEPTH_TEST); + driver->glDepthFunc(eGL_ALWAYS); + driver->glDepthMask(GL_TRUE); + driver->glDisable(eGL_BLEND); + // replay start + driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw); - // read and get number of fragments - uint32_t numFragments; - driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_INT, (void *)&numFragments); + uint32_t numFragments; + ModificationValue modValue; + PixelValue pixelValue; - eventFragments.emplace(modEvents[i].eventId, numFragments); + driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_INT, (void *)&numFragments); + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)pixelValue.floatValue.data()); + driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth); + modValue.col = pixelValue; - // restore the capture's framebuffer - driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); - } + // We're not reading the stencil value here, so use the postMod instead. + // Shaders don't actually output stencil values, those are determined by the stencil op. + modValue.stencil = history[i].postMod.stencil; + + history[i].shaderOut = modValue; + + eventFragments.emplace(modEvents[i].eventId, numFragments); + + // restore the capture's framebuffer + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); if(i < modEvents.size() - 1) { @@ -397,15 +599,13 @@ void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, GLPixelHistoryResources &resources, const EventUsage &modEvent, int x, int y, rdcarray &history, const std::map &eventFragments, - PerFragmentQueryType queryType) + PerFragmentQueryType queryType, bool usingLegacyPrimitiveId, + uint32_t numFragments) { driver->ReplayLog(0, modEvent.eventId - 1, eReplay_Full); driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); driver->glReadBuffer(eGL_COLOR_ATTACHMENT0); - auto it = eventFragments.find(modEvent.eventId); - uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0; - driver->glEnable(eGL_SCISSOR_TEST); driver->glScissor(x, y, 1, 1); @@ -433,6 +633,8 @@ void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, }); RDCASSERT(historyIndex != history.end()); + GLint currentProgram = 0; + if(queryType == PerFragmentQueryType::PostMod) { if(historyIndex != history.begin()) @@ -464,8 +666,6 @@ void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, driver->glClearColor(0.84f, 0.17f, 0.2f, 0.49f); driver->glClear(eGL_COLOR_BUFFER_BIT); - GLint currentProgram; - driver->glGetIntegerv(eGL_CURRENT_PROGRAM, ¤tProgram); driver->glUseProgram(GetPrimitiveIdProgram(driver, replay, resources, currentProgram)); @@ -476,19 +676,32 @@ void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, ModificationValue modValue; PixelValue pixelValue; modValue.stencil = 0; + // Set the stencil function so only jth fragment will pass. driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff); driver->glClear(eGL_STENCIL_BUFFER_BIT); - driver->ReplayLog(modEvent.eventId, modEvent.eventId, eReplay_Full); + driver->ReplayLog(modEvent.eventId, modEvent.eventId, eReplay_OnlyDraw); if(queryType == PerFragmentQueryType::PrimitiveId) { - int primitiveIds[4]; - driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)primitiveIds); + if(usingLegacyPrimitiveId) + { + float primitiveIds[4]; + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)primitiveIds); - RDCASSERT(primitiveIds[0] == primitiveIds[1] && primitiveIds[0] == primitiveIds[2] && - primitiveIds[0] == primitiveIds[3]); - historyIndex->primitiveID = primitiveIds[0]; + RDCASSERT(primitiveIds[0] == primitiveIds[1] && primitiveIds[0] == primitiveIds[2] && + primitiveIds[0] == primitiveIds[3]); + historyIndex->primitiveID = int(primitiveIds[0]); + } + else + { + int primitiveIds[4]; + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)primitiveIds); + + RDCASSERT(primitiveIds[0] == primitiveIds[1] && primitiveIds[0] == primitiveIds[2] && + primitiveIds[0] == primitiveIds[3]); + historyIndex->primitiveID = primitiveIds[0]; + } } else { @@ -508,43 +721,11 @@ void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, } ++historyIndex; } -} -void WriteClearValues(WrappedOpenGL *driver, GLPixelHistoryResources &resources, - const EventUsage clearEvent, int x, int y, rdcarray &history) -{ - driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); - driver->glClear(eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT | eGL_STENCIL_BUFFER_BIT); - driver->ReplayLog(0, clearEvent.eventId - 1, eReplay_WithoutDraw); - - GLint savedReadFramebuffer, savedDrawFramebuffer; - driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); - driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); - // bind our own framebuffer to save the pixel values - driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.frameBuffer); - driver->ReplayLog(clearEvent.eventId, clearEvent.eventId, eReplay_Full); - - // read the post mod pixel value into the history event - ModificationValue modValue; - PixelValue pixelValue; - driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)pixelValue.floatValue.data()); - driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth); - driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_INT, (void *)&modValue.stencil); - modValue.col = pixelValue; - - PixelModification referenceHistory; - referenceHistory.eventId = clearEvent.eventId; - auto historyIndex = - std::lower_bound(history.begin(), history.end(), referenceHistory, - [](const PixelModification &h1, const PixelModification &h2) -> bool { - return h1.eventId < h2.eventId; - }); - - historyIndex->postMod = modValue; - - // restore the capture's framebuffer - driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); + if(queryType == PerFragmentQueryType::PrimitiveId) + { + driver->glUseProgram(currentProgram); + } } bool depthTestPassed(int depthFunc, float shaderOutputDepth, float depthInBuffer) @@ -560,19 +741,28 @@ bool depthTestPassed(int depthFunc, float shaderOutputDepth, float depthInBuffer case eGL_GEQUAL: return shaderOutputDepth >= depthInBuffer; case eGL_ALWAYS: return true; - default: RDCASSERT(0); + default: RDCERR("Unexpected depth function: %d", depthFunc); } return false; } void CalculateFragmentDepthTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources, const rdcarray &modEvents, - rdcarray &history) + rdcarray &history, + const std::map &eventFragments) { driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw); size_t historyIndex = 0; for(size_t i = 0; i < modEvents.size(); ++i) { + // We only need to calculate the depth test for events with multiple fragments. + auto it = eventFragments.find(modEvents[i].eventId); + uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0; + if(numFragments <= 1) + { + continue; + } + for(; historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId; ++historyIndex) { @@ -649,7 +839,11 @@ rdcarray GLReplay::PixelHistory(rdcarray events, MakeCurrentReplayContext(&m_ReplayCtx); - PixelHistorySetupResources(m_pDriver, resources, textureDesc, sub, (uint32_t)events.size()); + int glslVersion = DebugData.glslVersion; + bool usingLegacyPrimitveId = glslVersion < 330; + + PixelHistorySetupResources(m_pDriver, resources, textureDesc, sub, (uint32_t)events.size(), + glslVersion); rdcarray modEvents = QueryModifyingEvents(m_pDriver, resources, events, x, flippedY, history); @@ -661,8 +855,10 @@ rdcarray GLReplay::PixelHistory(rdcarray events, } QueryFailedTests(m_pDriver, resources, modEvents, x, flippedY, history); + QueryPostModPixelValues(m_pDriver, resources, modEvents, x, flippedY, history); + std::map eventFragments = - QueryNumFragmentsByEvent(m_pDriver, resources, modEvents, x, flippedY); + QueryNumFragmentsByEvent(m_pDriver, resources, modEvents, history, x, flippedY); // copy history entries to create one history per fragment for(size_t h = 0; h < history.size();) @@ -686,18 +882,19 @@ rdcarray GLReplay::PixelHistory(rdcarray events, for(const EventUsage &modEvent : modEvents) { - if(modEvent.usage == ResourceUsage::Clear) - { - WriteClearValues(m_pDriver, resources, modEvent, x, flippedY, history); - } - else + auto it = eventFragments.find(modEvent.eventId); + uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0; + if(numFragments > 1) { QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, - eventFragments, PerFragmentQueryType::ShaderOut); + eventFragments, PerFragmentQueryType::ShaderOut, usingLegacyPrimitveId, + numFragments); QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, - eventFragments, PerFragmentQueryType::PrimitiveId); + eventFragments, PerFragmentQueryType::PrimitiveId, + usingLegacyPrimitveId, numFragments); QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, - eventFragments, PerFragmentQueryType::PostMod); + eventFragments, PerFragmentQueryType::PostMod, usingLegacyPrimitveId, + numFragments); } } @@ -707,7 +904,7 @@ rdcarray GLReplay::PixelHistory(rdcarray events, history[i].preMod = history[i - 1].postMod; } - CalculateFragmentDepthTests(m_pDriver, resources, modEvents, history); + CalculateFragmentDepthTests(m_pDriver, resources, modEvents, history, eventFragments); PixelHistoryDestroyResources(m_pDriver, resources); return history; diff --git a/renderdoc/renderdoc.vcxproj b/renderdoc/renderdoc.vcxproj index 00f5843f6..36870e853 100644 --- a/renderdoc/renderdoc.vcxproj +++ b/renderdoc/renderdoc.vcxproj @@ -700,6 +700,7 @@ + diff --git a/renderdoc/renderdoc.vcxproj.filters b/renderdoc/renderdoc.vcxproj.filters index 666f949e4..9b685605e 100644 --- a/renderdoc/renderdoc.vcxproj.filters +++ b/renderdoc/renderdoc.vcxproj.filters @@ -1030,6 +1030,9 @@ Resources\glsl + + Resources\glsl + Resources\glsl