diff --git a/renderdoc/driver/gl/gl_pixelhistory.cpp b/renderdoc/driver/gl/gl_pixelhistory.cpp index 2b71097d3..c8a98459f 100644 --- a/renderdoc/driver/gl/gl_pixelhistory.cpp +++ b/renderdoc/driver/gl/gl_pixelhistory.cpp @@ -38,6 +38,42 @@ bool isDirectWrite(ResourceUsage usage) usage == ResourceUsage::GenMips); } +struct FramebufferKey +{ + GLenum colorFormat; + GLenum depthFormat; + GLenum stencilFormat; + uint32_t numSamples; + bool operator<(const FramebufferKey &other) const + { + if(colorFormat < other.colorFormat) + { + return true; + } + if(colorFormat > other.colorFormat) + { + return false; + } + if(depthFormat < other.depthFormat) + { + return true; + } + if(depthFormat > other.depthFormat) + { + return false; + } + if(stencilFormat < other.stencilFormat) + { + return true; + } + if(stencilFormat > other.stencilFormat) + { + return false; + } + return numSamples < other.numSamples; + } +}; + struct CopyFramebuffer { GLuint framebufferId; @@ -47,20 +83,15 @@ struct CopyFramebuffer GLuint stencilTextureId; GLuint stencilViewId; uint32_t width; + FramebufferKey format; }; -struct FramebufferKey +struct ShaderOutFramebuffer { - GLenum depthFormat; - GLenum stencilFormat; - uint32_t numSamples; - bool operator<(const FramebufferKey &other) const - { - return depthFormat < other.depthFormat || - (depthFormat == other.depthFormat && stencilFormat < other.stencilFormat) || - (depthFormat == other.depthFormat && stencilFormat == other.stencilFormat && - numSamples < other.numSamples); - } + GLuint framebufferId; + GLuint colorTextureId; + GLuint dsTextureId; + FramebufferKey format; }; struct GLPixelHistoryResources @@ -77,6 +108,7 @@ struct GLPixelHistoryResources GLuint msCopyUniformBlockBuffer; std::unordered_map programs; std::map copyFramebuffers; + std::map shaderOutIntFramebuffers; }; enum class OpenGLTest @@ -121,8 +153,8 @@ GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay, // so that you can blit from the current bound framebuffer into the new framebuffer const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, std::map ©Framebuffers, - uint32_t numSamples, uint32_t numEvents, - GLenum depthFormat, GLenum stencilFormat) + uint32_t numSamples, uint32_t numEvents, GLenum depthFormat, + GLenum stencilFormat, GLenum colorFormat) { bool multisampled = numSamples > 1; @@ -145,7 +177,7 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType); - auto it = copyFramebuffers.find({depthFormat, stencilFormat, numSamples}); + auto it = copyFramebuffers.find({colorFormat, depthFormat, stencilFormat, numSamples}); if(it != copyFramebuffers.end()) { return it->second; @@ -165,14 +197,14 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, // Allocate a texture for the pixel history colour values GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D; driver->glGenTextures(1, ©Framebuffer.colorTextureId); - driver->CreateTextureImage(copyFramebuffer.colorTextureId, eGL_RGBA32F, eGL_NONE, eGL_NONE, + driver->CreateTextureImage(copyFramebuffer.colorTextureId, colorFormat, eGL_NONE, eGL_NONE, textureTarget, 2, copyFramebuffer.width, 1, 1, numSamples, 1); driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, copyFramebuffer.colorTextureId, 0); // Allocate a texture(s) for the pixel history depth/stencil values matching the capture's // frambebuffer's formats - if(curStencil == curDepth) + if(curStencil == curDepth && curStencil != 0) { driver->glGenTextures(1, ©Framebuffer.dsTextureId); driver->CreateTextureImage(copyFramebuffer.dsTextureId, depthFormat, eGL_NONE, eGL_NONE, @@ -211,11 +243,71 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); - FramebufferKey key = {depthFormat, stencilFormat, numSamples}; + FramebufferKey key = {colorFormat, depthFormat, stencilFormat, numSamples}; + copyFramebuffer.format = key; copyFramebuffers[key] = copyFramebuffer; return copyFramebuffers[key]; } +GLenum getTextureFormatType(GLenum internalFormat) +{ + static GLenum unsignedIntegerTextureFormatsArray[] = { + eGL_R8UI, eGL_R16UI, eGL_R32UI, eGL_RG8UI, eGL_RG16UI, eGL_RG32UI, eGL_RGB8UI, + eGL_RGB16UI, eGL_RGB32UI, eGL_RGBA8UI, eGL_RGBA16UI, eGL_RGBA32UI, eGL_RGB10_A2UI}; + static std::set unsignedIntegerTextureFormats( + unsignedIntegerTextureFormatsArray, + unsignedIntegerTextureFormatsArray + + (sizeof(unsignedIntegerTextureFormatsArray) / sizeof(GLenum))); + static GLenum signedIntegerTextureFormatsArray[] = { + eGL_R8I, eGL_R16I, eGL_R32I, eGL_RG8I, eGL_RG16I, eGL_RG32I, + eGL_RGB8I, eGL_RGB16I, eGL_RGB32I, eGL_RGBA8I, eGL_RGBA16I, eGL_RGBA32I}; + static std::set signedIntegerTextureFormats( + signedIntegerTextureFormatsArray, + signedIntegerTextureFormatsArray + (sizeof(signedIntegerTextureFormatsArray) / sizeof(GLenum))); + if(unsignedIntegerTextureFormats.count(internalFormat) != 0) + { + return eGL_UNSIGNED_INT; + } + if(signedIntegerTextureFormats.count(internalFormat) != 0) + { + return eGL_INT; + } + // There are other format types that this format could belong to, but they can be blitted between + // each other + return eGL_UNSIGNED_NORMALIZED; +} + +GLenum getCurrentTextureFormat(WrappedOpenGL *driver) +{ + GLuint curColor; + GLint colorType; + + driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, + eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + (GLint *)&curColor); + driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, + eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorType); + + GLenum colorFormat = eGL_NONE; + + if(curColor != 0) + { + ResourceId id; + if(colorType != eGL_RENDERBUFFER) + { + id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curColor)); + } + else + { + id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curColor)); + } + WrappedOpenGL::TextureData textureData = driver->m_Textures[id]; + colorFormat = textureData.internalFormat; + } + + return colorFormat; +} + const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, std::map ©Framebuffers, uint32_t numSamples, uint32_t numEvents) @@ -224,6 +316,8 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, GLint depthType; GLuint curStencil; GLint stencilType; + GLuint curColor; + GLint colorType; driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, @@ -239,6 +333,13 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType); + driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, + eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + (GLint *)&curColor); + + driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, + eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorType); + GLenum depthFormat = eGL_NONE; if(curDepth != 0) @@ -270,8 +371,73 @@ const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, stencilFormat = driver->m_Textures[id].internalFormat; } + GLenum colorFormat = eGL_NONE; + if(curColor != 0) + { + ResourceId id; + if(colorType != eGL_RENDERBUFFER) + { + id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curColor)); + } + else + { + id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curColor)); + } + WrappedOpenGL::TextureData textureData = driver->m_Textures[id]; + colorFormat = textureData.internalFormat; + } + return getCopyFramebuffer(driver, copyFramebuffers, numSamples, numEvents, depthFormat, - stencilFormat); + stencilFormat, colorFormat); +} + +ShaderOutFramebuffer getShaderOutFramebuffer(WrappedOpenGL *driver, + GLPixelHistoryResources &resources, FramebufferKey key, + uint32_t width, uint32_t height) +{ + std::map &shaderOutFramebuffers = + resources.shaderOutIntFramebuffers; + auto it = shaderOutFramebuffers.find(key); + + if(it != shaderOutFramebuffers.end()) + { + return it->second; + } + + GLint savedReadFramebuffer, savedDrawFramebuffer; + driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); + driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); + + bool multisampled = key.numSamples > 1; + GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D; + ShaderOutFramebuffer shaderOutFramebuffer; + RDCEraseEl(shaderOutFramebuffer); + + // Allocate a framebuffer that will render to the textures + driver->glGenFramebuffers(1, &shaderOutFramebuffer.framebufferId); + driver->glBindFramebuffer(eGL_FRAMEBUFFER, shaderOutFramebuffer.framebufferId); + + // Allocate a texture for the pixel history colour values + driver->glGenTextures(1, &shaderOutFramebuffer.colorTextureId); + driver->glBindTexture(textureTarget, shaderOutFramebuffer.colorTextureId); + driver->CreateTextureImage(shaderOutFramebuffer.colorTextureId, key.colorFormat, eGL_NONE, + eGL_NONE, textureTarget, 2, width, height, 1, key.numSamples, 1); + driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, + shaderOutFramebuffer.colorTextureId, 0); + + // Allocate a texture for the pixel history depth/stencil values + driver->glGenTextures(1, &shaderOutFramebuffer.dsTextureId); + driver->glBindTexture(textureTarget, shaderOutFramebuffer.dsTextureId); + driver->CreateTextureImage(shaderOutFramebuffer.dsTextureId, eGL_DEPTH24_STENCIL8, eGL_NONE, + eGL_NONE, textureTarget, 2, width, height, 1, key.numSamples, 1); + driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, + shaderOutFramebuffer.dsTextureId, 0); + + // restore the capture's framebuffer + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer); + + return (shaderOutFramebuffers[key] = shaderOutFramebuffer); } GLbitfield getFramebufferCopyMask(WrappedOpenGL *driver) @@ -411,6 +577,14 @@ bool PixelHistoryDestroyResources(WrappedOpenGL *driver, const GLPixelHistoryRes driver->glDeleteTextures(1, &cf.stencilViewId); } + for(const auto &pair : resources.shaderOutIntFramebuffers) + { + const ShaderOutFramebuffer &f = pair.second; + driver->glDeleteFramebuffers(1, &f.framebufferId); + driver->glDeleteTextures(1, &f.colorTextureId); + driver->glDeleteTextures(1, &f.dsTextureId); + } + return true; } @@ -621,16 +795,27 @@ void readPixelValuesMS(WrappedOpenGL *driver, const GLPixelHistoryResources &res void readPixelValues(WrappedOpenGL *driver, const GLPixelHistoryResources &resources, const CopyFramebuffer ©Framebuffer, rdcarray &history, - int historyIndex, bool readStencil, uint32_t numPixels) + int historyIndex, bool readStencil, uint32_t numPixels, bool isIntegerColour) { driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copyFramebuffer.framebufferId); - rdcarray colourValues; - colourValues.resize(4 * numPixels); + rdcarray intColourValues; + intColourValues.resize(4 * numPixels); + rdcarray floatColourValues; + floatColourValues.resize(4 * numPixels); rdcarray depthValues; depthValues.resize(numPixels); rdcarray stencilValues; stencilValues.resize(numPixels); - driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_RGBA, eGL_FLOAT, (void *)colourValues.data()); + if(isIntegerColour) + { + driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_RGBA_INTEGER, eGL_INT, + (void *)intColourValues.data()); + } + else + { + driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_RGBA, eGL_FLOAT, + (void *)floatColourValues.data()); + } if(copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0) { driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, @@ -649,7 +834,14 @@ void readPixelValues(WrappedOpenGL *driver, const GLPixelHistoryResources &resou for(int j = 0; j < 4; ++j) { - modValue.col.floatValue[j] = colourValues[i * 4 + j]; + if(isIntegerColour) + { + modValue.col.intValue[j] = intColourValues[i * 4 + j]; + } + else + { + modValue.col.floatValue[j] = floatColourValues[i * 4 + j]; + } } modValue.depth = depthValues[i]; @@ -697,12 +889,14 @@ void QueryPostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &res { CopyFramebuffer newCopyFramebuffer = getCopyFramebuffer( driver, resources.copyFramebuffers, 1 /*single sampled*/, int(modEvents.size())); + GLenum colourFormatType = getTextureFormatType(copyFramebuffer.format.colorFormat); + bool integerColour = colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT; if(newCopyFramebuffer.framebufferId != copyFramebuffer.framebufferId) { if(copyFramebuffer.framebufferId != ~0u) { readPixelValues(driver, resources, copyFramebuffer, history, lastHistoryIdx, true, - (uint32_t)(i - lastHistoryIdx)); + (uint32_t)(i - lastHistoryIdx), integerColour); } lastHistoryIdx = int(i); } @@ -728,8 +922,10 @@ void QueryPostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &res if(numSamples == 1 && copyFramebuffer.framebufferId != 0u) { + GLenum colourFormatType = getTextureFormatType(copyFramebuffer.format.colorFormat); + bool integerColour = colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT; readPixelValues(driver, resources, copyFramebuffer, history, lastHistoryIdx, true, - int(modEvents.size()) - lastHistoryIdx); + int(modEvents.size()) - lastHistoryIdx, integerColour); } } @@ -754,14 +950,45 @@ void readShaderOutMS(WrappedOpenGL *driver, const GLPixelHistoryResources &resou history[historyIndex].shaderOut = modValue; } +GLenum getShaderOutColourFormat(GLenum originalColourFormat) +{ + switch(originalColourFormat) + { + case eGL_RGBA8I: + case eGL_RGB8I: + case eGL_RG8I: + case eGL_R8I: return eGL_RGBA8I; + case eGL_RGBA8UI: + case eGL_RGB8UI: + case eGL_RG8UI: + case eGL_R8UI: return eGL_RGBA8UI; + case eGL_RGBA16I: + case eGL_RGB16I: + case eGL_RG16I: + case eGL_R16I: return eGL_RGBA16I; + case eGL_RGBA16UI: + case eGL_RGB16UI: + case eGL_RG16UI: + case eGL_R16UI: return eGL_RGBA16UI; + case eGL_RGBA32I: + case eGL_RGB32I: + case eGL_RG32I: + case eGL_R32I: return eGL_RGBA32I; + case eGL_RGBA32UI: + case eGL_RGB32UI: + case eGL_RG32UI: + case eGL_R32UI: return eGL_RGBA32UI; + case eGL_RGB10_A2UI: return eGL_RGB10_A2UI; + default: RDCERR("Unexpected colour format: %d", originalColourFormat); return eGL_NONE; + } +} + // This function a) calculates the number of fagments per event // and b) calculates the shader output values per event -std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, - GLPixelHistoryResources &resources, - const rdcarray &modEvents, - rdcarray &history, int x, - int y, uint32_t numSamples, - uint32_t sampleIndex) +std::map QueryNumFragmentsByEvent( + WrappedOpenGL *driver, GLPixelHistoryResources &resources, + const rdcarray &modEvents, rdcarray &history, int x, int y, + uint32_t numSamples, uint32_t sampleIndex, uint32_t width, uint32_t height) { driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw); @@ -772,8 +999,26 @@ std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, GLint savedReadFramebuffer, savedDrawFramebuffer; driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); - driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + + GLenum colourFormat = getCurrentTextureFormat(driver); + GLenum colourFormatType = getTextureFormatType(colourFormat); + GLenum shaderOutColourFormat = eGL_NONE; + + if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + { + shaderOutColourFormat = getShaderOutColourFormat(colourFormat); + FramebufferKey key = {shaderOutColourFormat, eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8, + numSamples}; + ShaderOutFramebuffer framebuffer = + getShaderOutFramebuffer(driver, resources, key, width, height); + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer.framebufferId); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, framebuffer.framebufferId); + } + else + { + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + } driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR); driver->glStencilMask(0xff); // default for 1 byte @@ -801,7 +1046,15 @@ std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, if(numSamples == 1) { ModificationValue modValue; - driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)modValue.col.floatValue.data()); + if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + { + driver->glReadPixels(x, y, 1, 1, eGL_RGBA_INTEGER, eGL_INT, + (void *)modValue.col.intValue.data()); + } + else + { + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)modValue.col.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_UNSIGNED_INT, (void *)&numFragments); @@ -813,11 +1066,15 @@ std::map QueryNumFragmentsByEvent(WrappedOpenGL *driver, } else { - const CopyFramebuffer ©Framebuffer = - getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, int(modEvents.size()), - eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8); + GLenum copyFramebufferColourFormat = + (colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + ? shaderOutColourFormat + : eGL_RGBA32F; + + const CopyFramebuffer ©Framebuffer = getCopyFramebuffer( + driver, resources.copyFramebuffers, numSamples, int(modEvents.size()), + eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8, copyFramebufferColourFormat); driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver), eGL_NEAREST); readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0, history, int(i)); @@ -961,7 +1218,8 @@ void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay, const rdcarray &modEvents, int x, int y, rdcarray &history, const std::map &eventFragments, - uint32_t numSamples, uint32_t sampleIndex) + uint32_t numSamples, uint32_t sampleIndex, uint32_t width, + uint32_t height) { driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw); for(size_t i = 0; i < modEvents.size(); ++i) @@ -1025,12 +1283,31 @@ void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay, driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); + GLenum colourFormat = getCurrentTextureFormat(driver); + GLenum colourFormatType = getTextureFormatType(colourFormat); + GLenum shaderOutColourFormat = eGL_NONE; + for(size_t j = 0; j < RDCMAX(numFragments, 1u); ++j) { // Set the stencil function so only jth fragment will pass. driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff); - driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + + if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + { + shaderOutColourFormat = getShaderOutColourFormat(colourFormat); + FramebufferKey key = {shaderOutColourFormat, eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8, + numSamples}; + ShaderOutFramebuffer framebuffer = + getShaderOutFramebuffer(driver, resources, key, width, height); + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer.framebufferId); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, framebuffer.framebufferId); + } + else + { + driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); + } + driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT); driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw); @@ -1038,7 +1315,17 @@ void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay, if(numSamples == 1) { ModificationValue modValue; - driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)modValue.col.floatValue.data()); + + if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + { + driver->glReadPixels(x, y, 1, 1, eGL_RGBA_INTEGER, eGL_INT, + (void *)modValue.col.intValue.data()); + } + else + { + driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, + (void *)modValue.col.floatValue.data()); + } driver->glReadPixels(x, y, 1, 1, eGL_DEPTH_COMPONENT, eGL_FLOAT, (void *)&modValue.depth); modValue.stencil = historyIndex->shaderOut.stencil; @@ -1046,11 +1333,15 @@ void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay, } else { - const CopyFramebuffer ©Framebuffer = - getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, - int(modEvents.size()), eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8); + GLenum copyFramebufferColourFormat = + (colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT) + ? shaderOutColourFormat + : eGL_RGBA32F; + + const CopyFramebuffer ©Framebuffer = getCopyFramebuffer( + driver, resources.copyFramebuffers, numSamples, int(modEvents.size()), + eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8, copyFramebufferColourFormat); driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId); - driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver), eGL_NEAREST); int oldStencil = historyIndex->shaderOut.stencil; @@ -1118,6 +1409,10 @@ void QueryPostModPerFragment(WrappedOpenGL *driver, GLReplay *replay, driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer); driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer); + GLenum colourFormat = getCurrentTextureFormat(driver); + GLenum colourFormatType = getTextureFormatType(colourFormat); + bool integerColour = colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT; + CopyFramebuffer copyFramebuffer; RDCEraseEl(copyFramebuffer); copyFramebuffer.framebufferId = ~0u; @@ -1157,7 +1452,7 @@ void QueryPostModPerFragment(WrappedOpenGL *driver, GLReplay *replay, { readPixelValues(driver, resources, copyFramebuffer, history, lastJ + int(historyIndex - history.begin()), false, - (uint32_t)(j - lastJ)); + (uint32_t)(j - lastJ), integerColour); } lastJ = int(j); } @@ -1176,7 +1471,8 @@ void QueryPostModPerFragment(WrappedOpenGL *driver, GLReplay *replay, if(numSamples == 1 && copyFramebuffer.framebufferId != ~0u) { readPixelValues(driver, resources, copyFramebuffer, history, - lastJ + int(historyIndex - history.begin()), true, numFragments - lastJ); + lastJ + int(historyIndex - history.begin()), true, numFragments - lastJ, + integerColour); } driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer); @@ -1270,8 +1566,8 @@ void QueryPrimitiveIdPerFragment(WrappedOpenGL *driver, GLReplay *replay, else { const CopyFramebuffer ©Framebuffer = - getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, - int(modEvents.size()), eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8); + getCopyFramebuffer(driver, resources.copyFramebuffers, numSamples, int(modEvents.size()), + eGL_DEPTH24_STENCIL8, eGL_DEPTH24_STENCIL8, eGL_RGBA32F); driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId); driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer); SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver), @@ -1441,8 +1737,11 @@ rdcarray GLReplay::PixelHistory(rdcarray events, QueryPostModPixelValues(m_pDriver, resources, modEvents, x, flippedY, history, textureDesc.msSamp, sampleIdx); - std::map eventFragments = QueryNumFragmentsByEvent( - m_pDriver, resources, modEvents, history, x, flippedY, textureDesc.msSamp, sampleIdx); + uint32_t textureWidth = textureDesc.width >> sub.mip; + uint32_t textureHeight = textureDesc.height >> sub.mip; + std::map eventFragments = + QueryNumFragmentsByEvent(m_pDriver, resources, modEvents, history, x, flippedY, + textureDesc.msSamp, sampleIdx, textureWidth, textureHeight); // copy history entries to create one history per fragment for(size_t h = 0; h < history.size();) @@ -1465,7 +1764,8 @@ rdcarray GLReplay::PixelHistory(rdcarray events, } QueryShaderOutPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history, - eventFragments, textureDesc.msSamp, sampleIdx); + eventFragments, textureDesc.msSamp, sampleIdx, textureWidth, + textureHeight); QueryPrimitiveIdPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history, eventFragments, usingFloatForPrimitiveId, textureDesc.msSamp, sampleIdx); diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt index c8fdae5cc..f7e855d56 100644 --- a/util/test/demos/CMakeLists.txt +++ b/util/test/demos/CMakeLists.txt @@ -103,6 +103,7 @@ set(OPENGL_SRC gl/gl_overlay_test.cpp gl/gl_parameter_zoo.cpp gl/gl_per_type_tex_units.cpp + gl/gl_pixelhistory_formats.cpp gl/gl_queries_in_use.cpp gl/gl_renderbuffer_zoo.cpp gl/gl_resource_lifetimes.cpp diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index c6c18b5c1..195d5b505 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -272,6 +272,7 @@ + true diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 1d78cd5a4..c3443183e 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -643,6 +643,9 @@ D3D12\demos + + OpenGL\demos + diff --git a/util/test/demos/gl/gl_pixelhistory_formats.cpp b/util/test/demos/gl/gl_pixelhistory_formats.cpp new file mode 100644 index 000000000..4ceab59fe --- /dev/null +++ b/util/test/demos/gl/gl_pixelhistory_formats.cpp @@ -0,0 +1,245 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2023 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 +#include "gl_test.h" + +RD_TEST(GL_PixelHistory_Formats, OpenGLGraphicsTest) +{ + static constexpr const char *Description = + "Draw a triangle to a variety of texture formats (to test pixel history)."; + + std::string common = R"EOSHADER( + +#version 420 core + +#define v2f v2f_block \ +{ \ + vec4 pos; \ + vec4 col; \ + vec4 uv; \ +} + +)EOSHADER"; + + std::string vertex = R"EOSHADER( + +layout(location = 0) in vec3 Position; +layout(location = 1) in vec4 Color; +layout(location = 2) in vec2 UV; + +out v2f vertOut; + +void main() +{ + vertOut.pos = vec4(Position.xyz, 1); + gl_Position = vertOut.pos; + vertOut.col = Color; + vertOut.uv = vec4(UV.xy, 0, 1); +} + +)EOSHADER"; + + std::string pixel = R"EOSHADER( + +in v2f vertIn; + +layout(location = 0, index = 0) out vec4 Color; + +void main() +{ + Color = vertIn.col; +} + +)EOSHADER"; + + int main() + { + // initialise, create window, create context, etc + if(!Init()) + return 3; + + GLuint vao = MakeVAO(); + glBindVertexArray(vao); + + GLuint vb = MakeBuffer(); + glBindBuffer(GL_ARRAY_BUFFER, vb); + const DefaultA2V DefaultTriWithALessBoringColour[3] = { + {Vec3f(-0.5f, -0.5f, 0.0f), Vec4f(0.57721f, 0.27182f, 0.1385f, 1.0f), Vec2f(0.0f, 0.0f)}, + {Vec3f(0.0f, 0.5f, 0.0f), Vec4f(0.57721f, 0.27182f, 0.1385f, 1.0f), Vec2f(0.0f, 1.0f)}, + {Vec3f(0.5f, -0.5f, 0.0f), Vec4f(0.57721f, 0.27182f, 0.1385f, 1.0f), Vec2f(1.0f, 0.0f)}, + }; + glBufferStorage(GL_ARRAY_BUFFER, sizeof(DefaultTriWithALessBoringColour), + DefaultTriWithALessBoringColour, 0); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), (void *)(0)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), (void *)(sizeof(Vec3f))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(DefaultA2V), + (void *)(sizeof(Vec3f) + sizeof(Vec4f))); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + + GLuint program = MakeProgram(common + vertex, common + pixel); + + GLenum colorFormats[] = { + GL_RGBA8, GL_RGBA16, GL_RGBA16F, GL_RGBA32F, GL_RGBA8I, GL_RGBA8UI, + GL_RGBA16I, GL_RGBA16UI, GL_RGBA32I, GL_RGBA32UI, GL_RGB8, GL_RGB16, + GL_RGB16F, GL_RGB32F, GL_RGB8I, GL_RGB8UI, GL_RG8, GL_RG16, + GL_RG16F, GL_RG32F, GL_RG8I, GL_RG8UI, GL_R8, GL_R16, + GL_R16F, GL_R32F, GL_R8I, GL_R8UI, GL_RGB5_A1, GL_RGB10_A2, + GL_RGB10_A2UI, GL_R11F_G11F_B10F, GL_RGB565}; + + GLenum depthFormats[] = {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32, + GL_DEPTH_COMPONENT32F, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8}; + + constexpr size_t colorFormatSize = sizeof(colorFormats) / sizeof(GLenum); + constexpr size_t depthFormatSize = sizeof(depthFormats) / sizeof(GLenum); + + GLuint colorTextures[colorFormatSize]; + GLuint multisampledColorTextures[colorFormatSize]; + GLuint depthTextures[depthFormatSize]; + + for(size_t i = 0; i < colorFormatSize; ++i) + { + colorTextures[i] = MakeTexture(); + multisampledColorTextures[i] = MakeTexture(); + glBindTexture(GL_TEXTURE_2D, colorTextures[i]); + glTexStorage2D(GL_TEXTURE_2D, 1, colorFormats[i], screenWidth, screenHeight); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, multisampledColorTextures[i]); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, colorFormats[i], screenWidth, + screenHeight, GL_TRUE); + } + + for(size_t i = 0; i < depthFormatSize; ++i) + { + depthTextures[i] = MakeTexture(); + glBindTexture(GL_TEXTURE_2D, depthTextures[i]); + glTexStorage2D(GL_TEXTURE_2D, 1, depthFormats[i], screenWidth, screenHeight); + } + + GLuint fbo = MakeFBO(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glDepthFunc(GL_ALWAYS); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + + glStencilFunc(GL_ALWAYS, 0xcc, 0xff); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glEnable(GL_STENCIL_TEST); + glStencilMask(0xff); + + while(Running()) + { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + for(size_t i = 0; i < colorFormatSize; ++i) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + colorTextures[i], 0); + + if(i >= depthFormatSize) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + } + else if(depthFormats[i] == GL_DEPTH24_STENCIL8 || depthFormats[i] == GL_DEPTH32F_STENCIL8) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, + depthTextures[i], 0); + } + else + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, + depthTextures[i], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + } + + GLenum bufs[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, bufs); + + float col[] = {0.2f, 0.2f, 0.2f, 1.0f}; + GLenum check = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(check != GL_FRAMEBUFFER_COMPLETE) + { + printf("%x\n", colorFormats[i]); + continue; + } + + glClearBufferfv(GL_COLOR, 0, col); + glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); + + glBindVertexArray(vao); + + glUseProgram(program); + + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight)); + + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + for(size_t i = 0; i < colorFormatSize; ++i) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, + multisampledColorTextures[i], 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + + GLenum bufs[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, bufs); + + float col[] = {0.2f, 0.2f, 0.2f, 1.0f}; + GLenum check = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if(check != GL_FRAMEBUFFER_COMPLETE) + { + printf("%x\n", colorFormats[i]); + continue; + } + + glClearBufferfv(GL_COLOR, 0, col); + + glBindVertexArray(vao); + + glUseProgram(program); + + glViewport(0, 0, GLsizei(screenWidth), GLsizei(screenHeight)); + + glDrawArrays(GL_TRIANGLES, 0, 3); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/tests/GL/GL_PixelHistory_Formats.py b/util/test/tests/GL/GL_PixelHistory_Formats.py new file mode 100644 index 000000000..03ffec0ce --- /dev/null +++ b/util/test/tests/GL/GL_PixelHistory_Formats.py @@ -0,0 +1,77 @@ +import renderdoc as rd +import rdtest +from typing import List + +def event_id(x): return x.eventId + +class GL_PixelHistory_Formats(rdtest.TestCase): + demos_test_name = 'GL_PixelHistory_Formats' + demos_frame_cap = 10 + + def check_capture(self): + action = self.get_first_action() + + while True: + glclear: rd.ActionDescription = self.find_action('glClear', action.eventId) + action: rd.ActionDescription = self.find_action('glDraw', action.eventId+1) + + if action is None: + break + + rdtest.log.success(f'Test {action.eventId} started.') + self.controller.SetFrameEvent(action.eventId, True) + + pipe: rd.PipeState = self.controller.GetPipelineState() + rt: rd.BoundResource = pipe.GetOutputTargets()[0] + tex: rd.ResourceId = rt.resourceId + sub = rd.Subresource() + textures: List[rd.TextureDescription] = self.controller.GetTextures() + def predicate(texDesc): + return texDesc.resourceId == tex + texDescription = next(filter(predicate, textures)) + + if texDescription.format.Name() == 'B5G5R5A1_UNORM' or texDescription.format.Name() == 'B5G6R5_UNORM': + eps = 1.0 / 32.0 + elif texDescription.format.Name() == 'R10G10B10A2_UNORM': + eps = 1.0 / 1024.0 + elif texDescription.format.Name() == 'R11G11B10_FLOAT': + eps = 0.01 + elif texDescription.format.compByteWidth == 1: + eps = 1.0 / 255.0 + else: + eps = 2.0 / 16384.0 + + + + x, y = 190, 149 + events = [glclear.eventId, action.eventId] + modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + self.check_events(events, modifs, False) + self.check_pixel_value(tex, x, y, modifs[-1].postMod.col.floatValue, sub=sub, cast=rt.typeCast, eps=eps) + self.check_shader_out_with_postmod(modifs[-1].shaderOut.col.floatValue, modifs[-1].postMod.col.floatValue, texDescription.format.compCount, eps) + + x, y = 328, 199 + events = [glclear.eventId] + modifs: List[rd.pixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.typeCast) + self.check_events(events, modifs, False) + self.check_pixel_value(tex, x, y, modifs[-1].postMod.col.floatValue, sub=sub, cast=rt.typeCast, eps=eps) + + rdtest.log.success('Test {} completed.'.format(action.eventId)) + + + def check_events(self, events, modifs, hasSecondary): + self.check(len(modifs) == len(events), "Expected {} events, got {}, modifs {}".format(len(events), len(modifs), modifs)) + + for i in range(len(modifs)): + self.check(modifs[i].eventId == events[i], f"Expected event with id {events[i]}, but got {modifs[i].eventId}") + + + + def check_shader_out_with_postmod(self, shaderOut, postMod, compCount: int, eps: float): + print(f"shaderOut: {shaderOut}, posMod: {postMod}") + + diff = 0 + + for i in range(compCount): + if not rdtest.value_compare(shaderOut[i], postMod[i], eps=eps): + raise rdtest.TestFailureException(f"shaderOut and postMod differ by {shaderOut[i] - postMod[i]}")