diff --git a/renderdoc/driver/gl/gl_pixelhistory.cpp b/renderdoc/driver/gl/gl_pixelhistory.cpp index 3bd9b1441..b5b73fbd9 100644 --- a/renderdoc/driver/gl/gl_pixelhistory.cpp +++ b/renderdoc/driver/gl/gl_pixelhistory.cpp @@ -22,6 +22,9 @@ * THE SOFTWARE. ******************************************************************************/ +#include +#include "data/glsl_shaders.h" +#include "gl_common.h" #include "gl_driver.h" #include "gl_replay.h" @@ -41,6 +44,8 @@ struct GLPixelHistoryResources GLuint colorImage; GLuint dsImage; GLuint frameBuffer; + GLuint primitiveIdFragmentShader; + std::unordered_map programs; }; enum class OpenGLTest @@ -52,6 +57,82 @@ enum class OpenGLTest NumTests }; +enum class PerFragmentQueryType +{ + ShaderOut, + PostMod, + PrimitiveId +}; + +GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay, + GLPixelHistoryResources &resources, GLuint currentProgram) +{ + auto programIterator = resources.programs.find(currentProgram); + if(programIterator != resources.programs.end()) + { + return programIterator->second; + } + + GLuint newProgram; + + 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); + + return newProgram; +} + bool PixelHistorySetupResources(WrappedOpenGL *driver, GLPixelHistoryResources &resources, const TextureDescription &desc, const Subresource &sub, uint32_t numEvents) @@ -75,6 +156,16 @@ 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); + return true; } @@ -83,6 +174,12 @@ bool PixelHistoryDestroyResources(WrappedOpenGL *driver, const GLPixelHistoryRes driver->glDeleteTextures(1, &resources.colorImage); driver->glDeleteTextures(1, &resources.dsImage); driver->glDeleteFramebuffers(1, &resources.frameBuffer); + driver->glDeleteShader(resources.primitiveIdFragmentShader); + + for(const std::pair &resourceProgram : resources.programs) + { + driver->glDeleteProgram(resourceProgram.second); + } return true; } @@ -98,7 +195,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))) { @@ -123,7 +220,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)) { @@ -136,9 +233,10 @@ rdcarray QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryR } else { - int numFragments; - driver->glGetQueryObjectiv(occlusionQueries[i], eGL_QUERY_RESULT, &numFragments); - if(numFragments > 0) + uint32_t numSamples; + driver->glGetQueryObjectuiv(occlusionQueries[i], eGL_QUERY_RESULT, &numSamples); + + if(numSamples > 0) { PixelModification mod; RDCEraseEl(mod); @@ -153,42 +251,51 @@ rdcarray QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryR return modEvents; } -void QueryPostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &resources, - const rdcarray &modEvents, int x, int y, - rdcarray &history) +std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, + GLPixelHistoryResources &resources, + const rdcarray &modEvents, int x, + int y) { 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++) + std::map eventFragments; + + 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); + 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); - // 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; + // read and get number of fragments + uint32_t numFragments; + driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_INT, (void *)&numFragments); - history[i].postMod = modValue; + eventFragments.emplace(modEvents[i].eventId, numFragments); - // restore the capture's framebuffer - driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); + // 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); } } + + return eventFragments; } bool QueryScissorTest(WrappedOpenGL *driver, GLPixelHistoryResources &resources, @@ -277,13 +384,226 @@ void QueryFailedTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources, } } + // ensure that history objects are one-to-one mapped with modEvent objects + RDCASSERT(history[i].eventId == modEvents[i].eventId); history[i].scissorClipped = failedTest == OpenGLTest::ScissorTest; history[i].stencilTestFailed = failedTest == OpenGLTest::StencilTest; history[i].depthTestFailed = failedTest == OpenGLTest::DepthTest; history[i].backfaceCulled = failedTest == OpenGLTest::FaceCulling; } } -}; + +void QueryPerFragmentValues(WrappedOpenGL *driver, GLReplay *replay, + GLPixelHistoryResources &resources, const EventUsage &modEvent, int x, + int y, rdcarray &history, + const std::map &eventFragments, + PerFragmentQueryType queryType) +{ + 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); + + driver->glClearStencil(0); + + if(queryType == PerFragmentQueryType::ShaderOut || queryType == PerFragmentQueryType::PrimitiveId) + { + driver->glEnable(eGL_DEPTH_TEST); + driver->glDepthFunc(eGL_ALWAYS); + driver->glDepthMask(GL_TRUE); + driver->glDisable(eGL_BLEND); + } + + driver->glStencilMask(0xff); + driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR); + driver->glEnable(eGL_STENCIL_TEST); + + PixelModification referenceHistory; + referenceHistory.eventId = modEvent.eventId; + + auto historyIndex = + std::lower_bound(history.begin(), history.end(), referenceHistory, + [](const PixelModification &h1, const PixelModification &h2) -> bool { + return h1.eventId < h2.eventId; + }); + RDCASSERT(historyIndex != history.end()); + + if(queryType == PerFragmentQueryType::PostMod) + { + if(historyIndex != history.begin()) + { + --historyIndex; + + // Because we are replaying this draw into our own framebuffer + // We need to set the values to the ones that they would be + // before this draw call. + driver->glClearColor( + historyIndex->postMod.col.floatValue[0], historyIndex->postMod.col.floatValue[1], + historyIndex->postMod.col.floatValue[2], historyIndex->postMod.col.floatValue[3]); + + GLboolean depthWriteMask = false; + driver->glGetBooleanv(eGL_DEPTH_WRITEMASK, &depthWriteMask); + driver->glDepthMask(true); + driver->glClearDepth(historyIndex->postMod.depth); + + driver->glClear(eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT); + + driver->glDepthMask(depthWriteMask); + ++historyIndex; + } + } + else if(queryType == PerFragmentQueryType::PrimitiveId) + { + // we expect this value to be overwritten by the primitive id shader. if not + // it will cause an assertion that all color values are the same to fail + 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)); + } + + for(size_t j = 0; j < std::max(numFragments, 1u); ++j) + { + ModificationValue modValue; + PixelValue pixelValue; + modValue.stencil = 0; + driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff); + driver->glClear(eGL_STENCIL_BUFFER_BIT); + + driver->ReplayLog(modEvent.eventId, modEvent.eventId, eReplay_Full); + + if(queryType == PerFragmentQueryType::PrimitiveId) + { + 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 + { + 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; + + if(queryType == PerFragmentQueryType::ShaderOut) + { + historyIndex->shaderOut = modValue; + } + else if(queryType == PerFragmentQueryType::PostMod) + { + historyIndex->postMod = modValue; + } + } + ++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); +} + +bool depthTestPassed(int depthFunc, float shaderOutputDepth, float depthInBuffer) +{ + switch(depthFunc) + { + case eGL_NEVER: return false; + case eGL_LESS: return shaderOutputDepth < depthInBuffer; + case eGL_EQUAL: return shaderOutputDepth == depthInBuffer; + case eGL_LEQUAL: return shaderOutputDepth <= depthInBuffer; + case eGL_GREATER: return shaderOutputDepth > depthInBuffer; + case eGL_NOTEQUAL: return shaderOutputDepth != depthInBuffer; + case eGL_GEQUAL: return shaderOutputDepth >= depthInBuffer; + case eGL_ALWAYS: return true; + + default: RDCASSERT(0); + } + return false; +} + +void CalculateFragmentDepthTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources, + const rdcarray &modEvents, + rdcarray &history) +{ + driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw); + size_t historyIndex = 0; + for(size_t i = 0; i < modEvents.size(); ++i) + { + for(; historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId; + ++historyIndex) + { + if(historyIndex == 0) + { + continue; + } + + if(driver->glIsEnabled(eGL_DEPTH_TEST)) + { + int depthFunc; + driver->glGetIntegerv(eGL_DEPTH_FUNC, &depthFunc); + + history[historyIndex].depthTestFailed = !depthTestPassed( + depthFunc, history[historyIndex].shaderOut.depth, history[historyIndex].preMod.depth); + } + else + { + // since there is no depth test, there is no failure. + history[historyIndex].depthTestFailed = false; + } + } + + if(i < modEvents.size() - 1) + { + driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw); + } + } +} + +}; // end of anonymous namespace rdcarray GLReplay::PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, @@ -341,7 +661,45 @@ 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); + + // copy history entries to create one history per fragment + for(size_t h = 0; h < history.size();) + { + uint32_t frags = 1; + auto it = eventFragments.find(history[h].eventId); + if(it != eventFragments.end()) + { + frags = it->second; + } + for(uint32_t f = 1; f < frags; ++f) + { + history.insert(h + 1, history[h]); + } + for(uint32_t f = 0; f < frags; ++f) + { + history[h + f].fragIndex = f; + } + h += RDCMAX(1u, frags); + } + + for(const EventUsage &modEvent : modEvents) + { + if(modEvent.usage == ResourceUsage::Clear) + { + WriteClearValues(m_pDriver, resources, modEvent, x, flippedY, history); + } + else + { + QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, + eventFragments, PerFragmentQueryType::ShaderOut); + QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, + eventFragments, PerFragmentQueryType::PrimitiveId); + QueryPerFragmentValues(m_pDriver, this, resources, modEvent, x, flippedY, history, + eventFragments, PerFragmentQueryType::PostMod); + } + } // copy the postMod to next history's preMod for(size_t i = 1; i < history.size(); ++i) @@ -349,6 +707,8 @@ rdcarray GLReplay::PixelHistory(rdcarray events, history[i].preMod = history[i - 1].postMod; } + CalculateFragmentDepthTests(m_pDriver, resources, modEvents, history); + PixelHistoryDestroyResources(m_pDriver, resources); return history; }