Files
renderdoc/renderdoc/driver/gl/gl_pixelhistory.cpp
T
baldurk 74ecbd235a Explicitly fetch pre/postmod values per-fragment with stencil counting
* This is more precise than propagating pre/post mod
2025-10-27 11:50:18 +00:00

2518 lines
96 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2025 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 <algorithm>
#include "data/glsl_shaders.h"
#include "maths/formatpacking.h"
#include "gl_common.h"
#include "gl_driver.h"
#include "gl_replay.h"
namespace
{
bool isDirectWrite(ResourceUsage usage)
{
return ((usage >= ResourceUsage::VS_RWResource && usage <= ResourceUsage::CS_RWResource) ||
usage == ResourceUsage::CopyDst || usage == ResourceUsage::Copy ||
usage == ResourceUsage::Resolve || usage == ResourceUsage::ResolveDst ||
usage == ResourceUsage::GenMips);
}
enum class ModType
{
PreMod,
PostMod,
};
struct FramebufferKey
{
ModType modType;
GLenum colorFormat;
GLenum depthFormat;
GLenum stencilFormat;
uint32_t numSamples;
bool operator<(const FramebufferKey &other) const
{
if(modType != other.modType)
return modType < other.modType;
if(colorFormat != other.colorFormat)
return colorFormat < other.colorFormat;
if(depthFormat != other.depthFormat)
return depthFormat < other.depthFormat;
if(stencilFormat != other.stencilFormat)
return stencilFormat < other.stencilFormat;
return numSamples < other.numSamples;
}
};
struct CopyFramebuffer
{
GLuint framebufferId;
GLuint colorTextureId;
GLuint dsTextureId;
GLuint depthTextureId;
GLuint stencilTextureId;
GLuint stencilViewId;
uint32_t width;
FramebufferKey format;
};
struct ShaderOutFramebuffer
{
GLuint framebufferId;
GLuint colorTextureId;
GLuint dsTextureId;
FramebufferKey format;
};
struct GLPixelHistoryResources
{
ResourceId target;
bool depthTarget;
// a framebuffer with the target resource bound, used for copying for pre/post mod on direct writes
// (compute dispatches or clears) where the currently bound framebuffer won't necessarily match
GLuint copySourceFramebuffer;
// Used for offscreen rendering for draw call events.
GLuint fullPrecisionColorImage;
GLuint fullPrecisionDsImage;
GLuint fullPrecisionFrameBuffer;
GLuint primitiveIdFragmentShader;
GLuint primitiveIdFragmentShaderSPIRV;
GLuint fixedColFragmentShader;
GLuint fixedColFragmentShaderSPIRV;
GLuint msCopyComputeProgram;
GLuint msCopyDepthComputeProgram;
GLuint msCopyDstBuffer;
GLuint msCopyUniformBlockBuffer;
std::unordered_map<GLuint, GLuint> primIdPrograms;
std::unordered_map<GLuint, GLuint> fixedColPrograms;
std::map<FramebufferKey, CopyFramebuffer> copyFramebuffers;
std::map<FramebufferKey, ShaderOutFramebuffer> shaderOutIntFramebuffers;
};
enum class OpenGLTest
{
FaceCulling,
ScissorTest,
DepthClamp,
DepthBounds,
Discard,
StencilTest,
DepthTest,
SampleMask,
NumTests
};
enum class PerFragmentQueryType
{
ShaderOut,
PostMod,
PrimitiveId
};
GLuint GetFixedColProgram(WrappedOpenGL *driver, GLReplay *replay,
GLPixelHistoryResources &resources, GLuint currentProgram)
{
auto programIterator = resources.fixedColPrograms.find(currentProgram);
if(programIterator != resources.fixedColPrograms.end())
{
return programIterator->second;
}
GLRenderState rs;
rs.FetchState(driver);
GLuint ret = driver->glCreateProgram();
replay->CreateFragmentShaderReplacementProgram(rs.Program.name, ret, rs.Pipeline.name,
resources.fixedColFragmentShader,
resources.fixedColFragmentShaderSPIRV);
resources.fixedColPrograms[currentProgram] = ret;
return ret;
}
GLuint GetPrimitiveIdProgram(WrappedOpenGL *driver, GLReplay *replay,
GLPixelHistoryResources &resources, GLuint currentProgram)
{
auto programIterator = resources.primIdPrograms.find(currentProgram);
if(programIterator != resources.primIdPrograms.end())
{
return programIterator->second;
}
GLRenderState rs;
rs.FetchState(driver);
GLuint ret = driver->glCreateProgram();
replay->CreateFragmentShaderReplacementProgram(rs.Program.name, ret, rs.Pipeline.name,
resources.primitiveIdFragmentShader,
resources.primitiveIdFragmentShaderSPIRV);
resources.primIdPrograms[currentProgram] = ret;
return ret;
}
// Returns a Framebuffer that has the same depth and stencil formats of the currently bound
// framebuffer
// so that you can blit from the current bound framebuffer into the new framebuffer
const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver,
std::map<FramebufferKey, CopyFramebuffer> &copyFramebuffers,
ModType modType, uint32_t numSamples, uint32_t numEvents,
GLenum depthFormat, GLenum stencilFormat,
GLenum colorFormat)
{
bool multisampled = numSamples > 1;
GLuint curDepth;
GLint depthType;
GLuint curStencil;
GLint stencilType;
if(colorFormat == eGL_NONE)
colorFormat = eGL_RGBA8;
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType);
FramebufferKey key = {modType, colorFormat, depthFormat, stencilFormat, numSamples};
auto it = copyFramebuffers.find(key);
if(it != copyFramebuffers.end())
{
return it->second;
}
GLint savedReadFramebuffer, savedDrawFramebuffer;
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
CopyFramebuffer copyFramebuffer;
RDCEraseEl(copyFramebuffer);
copyFramebuffer.width = RDCMAX(100U, numEvents);
// Allocate a framebuffer that will render to the textures
driver->glGenFramebuffers(1, &copyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_FRAMEBUFFER, copyFramebuffer.framebufferId);
// Allocate a texture for the pixel history colour values
GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D;
driver->glGenTextures(1, &copyFramebuffer.colorTextureId);
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 && curStencil != 0)
{
driver->glGenTextures(1, &copyFramebuffer.dsTextureId);
driver->CreateTextureImage(copyFramebuffer.dsTextureId, depthFormat, eGL_NONE, eGL_NONE,
textureTarget, 2, copyFramebuffer.width, 1, 1, numSamples, 1);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT,
copyFramebuffer.dsTextureId, 0);
if(multisampled)
{
driver->glGenTextures(1, &copyFramebuffer.stencilViewId);
driver->glTextureView(copyFramebuffer.stencilViewId, textureTarget,
copyFramebuffer.dsTextureId, depthFormat, 0, 1, 0, 1);
}
}
else
{
if(curDepth != 0)
{
driver->glGenTextures(1, &copyFramebuffer.depthTextureId);
driver->CreateTextureImage(copyFramebuffer.depthTextureId, depthFormat, eGL_NONE, eGL_NONE,
textureTarget, 2, copyFramebuffer.width, 1, 1, numSamples, 1);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
copyFramebuffer.depthTextureId, 0);
}
if(curStencil != 0)
{
driver->glGenTextures(1, &copyFramebuffer.stencilTextureId);
driver->CreateTextureImage(copyFramebuffer.stencilTextureId, stencilFormat, eGL_NONE, eGL_NONE,
textureTarget, 2, copyFramebuffer.width, 1, 1, numSamples, 1);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
copyFramebuffer.stencilTextureId, 0);
}
}
// restore the capture's framebuffer
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
copyFramebuffer.format = key;
copyFramebuffers[key] = copyFramebuffer;
return copyFramebuffers[key];
}
GLenum getTextureFormatType(GLenum internalFormat)
{
if(IsUIntFormat(internalFormat))
{
return eGL_UNSIGNED_INT;
}
if(IsSIntFormat(internalFormat))
{
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, uint32_t colIdx)
{
GLuint curColor;
GLint colorType;
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curColor);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER,
GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
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));
}
colorFormat = driver->m_Textures[id].internalFormat;
}
return colorFormat;
}
const CopyFramebuffer &getCopyFramebuffer(WrappedOpenGL *driver, uint32_t colIdx,
std::map<FramebufferKey, CopyFramebuffer> &copyFramebuffers,
ModType modType, uint32_t numSamples, uint32_t numEvents)
{
GLuint curDepth;
GLint depthType;
GLuint curStencil;
GLint stencilType;
GLuint curColor;
GLint colorType;
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &stencilType);
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curColor);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER,
GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &colorType);
GLenum depthFormat = eGL_NONE;
if(curDepth != 0)
{
ResourceId id;
if(depthType != eGL_RENDERBUFFER)
{
id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curDepth));
}
else
{
id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curDepth));
}
depthFormat = driver->m_Textures[id].internalFormat;
}
GLenum stencilFormat = eGL_NONE;
if(curStencil != 0)
{
ResourceId id;
if(stencilType != eGL_RENDERBUFFER)
{
id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curStencil));
}
else
{
id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curStencil));
}
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));
}
colorFormat = driver->m_Textures[id].internalFormat;
}
return getCopyFramebuffer(driver, copyFramebuffers, modType, numSamples, numEvents, depthFormat,
stencilFormat, colorFormat);
}
ShaderOutFramebuffer getShaderOutFramebuffer(WrappedOpenGL *driver, uint32_t colIdx,
GLPixelHistoryResources &resources, FramebufferKey key,
uint32_t width, uint32_t height)
{
std::map<FramebufferKey, ShaderOutFramebuffer> &shaderOutFramebuffers =
resources.shaderOutIntFramebuffers;
auto it = shaderOutFramebuffers.find(key);
if(it != shaderOutFramebuffers.end())
{
driver->glBindFramebuffer(eGL_FRAMEBUFFER, it->second.framebufferId);
for(uint32_t i = 0; i < 8; i++)
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i), 0, 0);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
it->second.colorTextureId, 0);
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, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
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_DEPTH32F_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)
{
GLuint curDepth = 0;
GLuint curStencil = 0;
GLuint curColor = 0;
GLint colorAttachment;
driver->glGetIntegerv(eGL_READ_BUFFER, &colorAttachment);
if(colorAttachment)
{
driver->glGetFramebufferAttachmentParameteriv(eGL_READ_FRAMEBUFFER, GLenum(colorAttachment),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curColor);
}
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
GLbitfield mask = 0;
if(curColor)
{
mask |= eGL_COLOR_BUFFER_BIT;
}
if(curDepth)
{
mask |= eGL_DEPTH_BUFFER_BIT;
}
if(curStencil)
{
mask |= eGL_STENCIL_BUFFER_BIT;
}
return mask;
}
uint32_t getFramebufferColIndex(WrappedOpenGL *driver, ResourceId target)
{
WrappedOpenGL &drv = *driver;
GLResourceManager *rm = driver->GetResourceManager();
GLuint numCols = 8;
drv.glGetIntegerv(eGL_MAX_COLOR_ATTACHMENTS, (GLint *)&numCols);
GLuint curCol[32] = {0};
GLuint curDepth = 0;
GLuint curStencil = 0;
bool rbCol[32] = {false};
bool rbDepth = false;
bool rbStencil = false;
GLenum type = eGL_TEXTURE;
for(GLuint i = 0; i < numCols; i++)
{
drv.glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curCol[i]);
drv.glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + i),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbCol[i] = true;
}
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbDepth = true;
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
drv.glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, (GLint *)&type);
if(type == eGL_RENDERBUFFER)
rbStencil = true;
for(GLuint i = 0; i < numCols; i++)
{
ResourceId id = rm->GetResID(rbCol[i] ? RenderbufferRes(driver->GetCtx(), curCol[i])
: TextureRes(driver->GetCtx(), curCol[i]));
if(id == target)
return i;
}
ResourceId depth = rm->GetResID(rbDepth ? RenderbufferRes(driver->GetCtx(), curDepth)
: TextureRes(driver->GetCtx(), curDepth));
ResourceId stencil = rm->GetResID(rbStencil ? RenderbufferRes(driver->GetCtx(), curStencil)
: TextureRes(driver->GetCtx(), curStencil));
// silently return attachment 0, it doesn't matter which colour attachment we use
if(depth == target || stencil == target)
return 0;
RDCWARN("Couldn't find attachment %s anywhere in this framebuffer", ToStr(target).c_str());
return 0;
}
bool PixelHistorySetupResources(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const TextureDescription &desc, const Subresource &sub,
uint32_t numEvents, GLuint glslVersion, uint32_t numSamples)
{
driver->glGenFramebuffers(1, &resources.copySourceFramebuffer);
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.copySourceFramebuffer);
GLResource targetTex = driver->GetResourceManager()->GetLiveResource(desc.resourceId);
GLenum targetAtt = eGL_COLOR_ATTACHMENT0;
resources.depthTarget = false;
if(desc.format.type == ResourceFormatType::D16S8 ||
desc.format.type == ResourceFormatType::D24S8 || desc.format.type == ResourceFormatType::D32S8)
{
targetAtt = eGL_DEPTH_STENCIL_ATTACHMENT;
resources.depthTarget = true;
}
else if(desc.format.compType == CompType::Depth)
{
targetAtt = eGL_DEPTH_ATTACHMENT;
resources.depthTarget = true;
}
else if(desc.format.type == ResourceFormatType::S8)
{
targetAtt = eGL_STENCIL_ATTACHMENT;
resources.depthTarget = true;
}
if(targetTex.Namespace == eResRenderbuffer)
{
driver->glFramebufferRenderbuffer(eGL_FRAMEBUFFER, targetAtt, eGL_RENDERBUFFER, targetTex.name);
}
else
{
if(desc.arraysize > 1)
driver->glFramebufferTextureLayer(eGL_FRAMEBUFFER, targetAtt, targetTex.name, sub.mip,
sub.slice);
else
driver->glFramebufferTexture(eGL_FRAMEBUFFER, targetAtt, targetTex.name, sub.mip);
}
// Allocate a framebuffer that will render to the textures
driver->glGenFramebuffers(1, &resources.fullPrecisionFrameBuffer);
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
// Allocate a texture for the pixel history colour values
bool multisampled = numSamples > 1;
GLenum textureTarget = multisampled ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D;
driver->glGenTextures(1, &resources.fullPrecisionColorImage);
driver->glBindTexture(textureTarget, resources.fullPrecisionColorImage);
driver->CreateTextureImage(resources.fullPrecisionColorImage, eGL_RGBA32F, eGL_NONE, eGL_NONE,
textureTarget, 2, desc.width >> sub.mip, desc.height >> sub.mip, 1,
numSamples, 1);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0,
resources.fullPrecisionColorImage, 0);
// Allocate a texture for the pixel history depth/stencil values
driver->glGenTextures(1, &resources.fullPrecisionDsImage);
driver->glBindTexture(textureTarget, resources.fullPrecisionDsImage);
driver->CreateTextureImage(resources.fullPrecisionDsImage, eGL_DEPTH32F_STENCIL8, eGL_NONE,
eGL_NONE, textureTarget, 2, desc.width >> sub.mip,
desc.height >> sub.mip, 1, numSamples, 1);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT,
resources.fullPrecisionDsImage, 0);
// We will blit the color values to this texture and then use a compute shader to get the values
// for the sample that we want
driver->glGenBuffers(1, &resources.msCopyDstBuffer);
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, resources.msCopyDstBuffer);
driver->glNamedBufferDataEXT(
resources.msCopyDstBuffer,
8 * (sizeof(float)) * numEvents, // 8 floats per event (r,g,b,a,depth,null,null,null)
NULL, eGL_DYNAMIC_READ);
driver->glGenBuffers(1, &resources.msCopyUniformBlockBuffer);
driver->glBindBuffer(eGL_UNIFORM_BUFFER, resources.msCopyUniformBlockBuffer);
driver->glNamedBufferDataEXT(resources.msCopyUniformBlockBuffer, 8 * sizeof(uint32_t), NULL,
eGL_DYNAMIC_DRAW);
// If the GLSL version is greater than or equal to 330, we can use IntBitsToFloat, otherwise we
// need to write the float value directly.
ShaderType shaderType = IsGLES ? ShaderType::GLSLES : ShaderType::GLSL;
rdcstr primIdGLSL;
if(glslVersion >= 330 || (IsGLES && glslVersion >= 300))
{
primIdGLSL = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), shaderType,
glslVersion);
}
else
{
primIdGLSL = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag), shaderType,
glslVersion, "#define INT_BITS_TO_FLOAT_NOT_SUPPORTED\n");
}
// SPIR-V shaders are always generated as desktop GL 430, for ease
rdcstr primIdSPIRV = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_primid_frag),
ShaderType::GLSPIRV, 430);
rdcstr fixedColGLSL =
GenerateGLSLShader(GetEmbeddedResource(glsl_fixedcol_frag), shaderType, glslVersion);
rdcstr fixedColSPIRV =
GenerateGLSLShader(GetEmbeddedResource(glsl_fixedcol_frag), ShaderType::GLSPIRV, 430);
rdcstr msCopySource = GenerateGLSLShader(GetEmbeddedResource(glsl_pixelhistory_mscopy_comp),
shaderType, glslVersion);
rdcstr msCopySourceDepth = GenerateGLSLShader(
GetEmbeddedResource(glsl_pixelhistory_mscopy_depth_comp), shaderType, glslVersion);
if(HasExt[ARB_gl_spirv])
{
resources.primitiveIdFragmentShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, primIdSPIRV);
resources.fixedColFragmentShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, fixedColSPIRV);
}
resources.primitiveIdFragmentShader = CreateShader(eGL_FRAGMENT_SHADER, primIdGLSL);
resources.fixedColFragmentShader = CreateShader(eGL_FRAGMENT_SHADER, fixedColGLSL);
resources.msCopyComputeProgram = CreateCShaderProgram(msCopySource);
resources.msCopyDepthComputeProgram = CreateCShaderProgram(msCopySourceDepth);
return true;
}
bool PixelHistoryDestroyResources(WrappedOpenGL *driver, const GLPixelHistoryResources &resources)
{
driver->glDeleteTextures(1, &resources.fullPrecisionColorImage);
driver->glDeleteTextures(1, &resources.fullPrecisionDsImage);
driver->glDeleteFramebuffers(1, &resources.copySourceFramebuffer);
driver->glDeleteFramebuffers(1, &resources.fullPrecisionFrameBuffer);
driver->glDeleteShader(resources.primitiveIdFragmentShader);
driver->glDeleteShader(resources.primitiveIdFragmentShaderSPIRV);
driver->glDeleteProgram(resources.msCopyComputeProgram);
driver->glDeleteProgram(resources.msCopyDepthComputeProgram);
driver->glDeleteBuffers(1, &resources.msCopyDstBuffer);
driver->glDeleteBuffers(1, &resources.msCopyUniformBlockBuffer);
for(const std::pair<const GLuint, GLuint> &resourceProgram : resources.primIdPrograms)
driver->glDeleteProgram(resourceProgram.second);
for(const std::pair<const GLuint, GLuint> &resourceProgram : resources.fixedColPrograms)
driver->glDeleteProgram(resourceProgram.second);
for(const auto &pair : resources.copyFramebuffers)
{
const CopyFramebuffer &cf = pair.second;
driver->glDeleteFramebuffers(1, &cf.framebufferId);
driver->glDeleteTextures(1, &cf.colorTextureId);
driver->glDeleteTextures(1, &cf.dsTextureId);
driver->glDeleteTextures(1, &cf.depthTextureId);
driver->glDeleteTextures(1, &cf.stencilTextureId);
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;
}
void CopyMSSample(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
const CopyFramebuffer &copyFramebuffer, int sampleIdx, int x, int y,
float *pixelDst)
{
GLint savedProgram;
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &savedProgram);
GLint savedActiveTexture;
driver->glGetIntegerv(eGL_ACTIVE_TEXTURE, &savedActiveTexture);
GLint savedShaderStorageBuffer;
driver->glGetIntegerv(eGL_SHADER_STORAGE_BUFFER_BINDING, &savedShaderStorageBuffer);
GLint savedUniformBuffer;
driver->glGetIntegerv(eGL_UNIFORM_BUFFER_BINDING, &savedUniformBuffer);
struct Bufbind
{
GLuint buf;
GLuint64 start;
GLuint64 size;
} ubo3, ssbo2;
GL.glGetIntegeri_v(eGL_UNIFORM_BUFFER_BINDING, 3, (GLint *)&ubo3.buf);
GL.glGetInteger64i_v(eGL_UNIFORM_BUFFER_START, 3, (GLint64 *)&ubo3.start);
GL.glGetInteger64i_v(eGL_UNIFORM_BUFFER_SIZE, 3, (GLint64 *)&ubo3.size);
GL.glGetIntegeri_v(eGL_SHADER_STORAGE_BUFFER_BINDING, 2, (GLint *)&ssbo2.buf);
GL.glGetInteger64i_v(eGL_SHADER_STORAGE_BUFFER_START, 2, (GLint64 *)&ssbo2.start);
GL.glGetInteger64i_v(eGL_SHADER_STORAGE_BUFFER_SIZE, 2, (GLint64 *)&ssbo2.size);
driver->glUseProgram(resources.msCopyComputeProgram);
GLint srcMSLoc = driver->glGetUniformLocation(resources.msCopyComputeProgram, "srcMS");
driver->glUniform1i(srcMSLoc, 0);
uint32_t uniforms[4] = {uint32_t(sampleIdx), uint32_t(x), uint32_t(y),
0}; // { sampleIdx, x, y, dstOffset }
driver->glBindBuffer(eGL_UNIFORM_BUFFER, resources.msCopyUniformBlockBuffer);
driver->glNamedBufferSubDataEXT(resources.msCopyUniformBlockBuffer, 0, sizeof(uniforms), uniforms);
driver->glBindBufferBase(eGL_UNIFORM_BUFFER, 3, resources.msCopyUniformBlockBuffer);
driver->glActiveTexture(eGL_TEXTURE0);
GLint savedMSTexture0;
driver->glGetIntegerv(eGL_TEXTURE_BINDING_2D_MULTISAMPLE, &savedMSTexture0);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.colorTextureId);
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, resources.msCopyDstBuffer);
driver->glBindBufferBase(eGL_SHADER_STORAGE_BUFFER, 2, resources.msCopyDstBuffer);
driver->glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
driver->glDispatchCompute(1, 1, 1);
driver->glUseProgram(resources.msCopyDepthComputeProgram);
GLint depthMSLoc = driver->glGetUniformLocation(resources.msCopyDepthComputeProgram, "depthMS");
GLint stencilMSLoc =
driver->glGetUniformLocation(resources.msCopyDepthComputeProgram, "stencilMS");
driver->glUniform1i(depthMSLoc, 0);
driver->glUniform1i(stencilMSLoc, 1);
// { sampleIdx, x, y, dstOffset, hasDepth, hasStencil }
uint32_t newUniforms[6] = {
uint32_t(sampleIdx),
uint32_t(x),
uint32_t(y),
1,
copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0,
copyFramebuffer.dsTextureId != 0 || copyFramebuffer.stencilTextureId != 0,
};
driver->glNamedBufferSubDataEXT(resources.msCopyUniformBlockBuffer, 0, sizeof(newUniforms),
newUniforms);
driver->glActiveTexture(eGL_TEXTURE1);
GLint savedMSTexture1;
driver->glGetIntegerv(eGL_TEXTURE_BINDING_2D_MULTISAMPLE, &savedMSTexture1);
if(copyFramebuffer.dsTextureId)
{
driver->glActiveTexture(eGL_TEXTURE0);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.dsTextureId);
driver->glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE, eGL_DEPTH_STENCIL_TEXTURE_MODE,
eGL_DEPTH_COMPONENT);
driver->glActiveTexture(eGL_TEXTURE1);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.stencilViewId);
driver->glTexParameteri(eGL_TEXTURE_2D_MULTISAMPLE, eGL_DEPTH_STENCIL_TEXTURE_MODE,
eGL_STENCIL_INDEX);
}
else
{
driver->glActiveTexture(eGL_TEXTURE0);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.depthTextureId);
driver->glActiveTexture(eGL_TEXTURE1);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, copyFramebuffer.stencilTextureId);
}
driver->glDispatchCompute(1, 1, 1);
driver->glMemoryBarrier(eGL_SHADER_STORAGE_BARRIER_BIT);
driver->glGetBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, 8 * 4, pixelDst);
if(ubo3.buf == 0 || (ubo3.start == 0 && ubo3.size == 0))
GL.glBindBufferBase(eGL_UNIFORM_BUFFER, 3, ubo3.buf);
else
GL.glBindBufferRange(eGL_UNIFORM_BUFFER, 3, ubo3.buf, (GLintptr)ubo3.start,
(GLsizeiptr)ubo3.size);
if(ssbo2.buf == 0 || (ssbo2.start == 0 && ssbo2.size == 0))
GL.glBindBufferBase(eGL_SHADER_STORAGE_BUFFER, 2, ssbo2.buf);
else
GL.glBindBufferRange(eGL_SHADER_STORAGE_BUFFER, 2, ssbo2.buf, (GLintptr)ssbo2.start,
(GLsizeiptr)ssbo2.size);
driver->glUseProgram(savedProgram);
driver->glActiveTexture(eGL_TEXTURE0);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, savedMSTexture0);
driver->glActiveTexture(eGL_TEXTURE1);
driver->glBindTexture(eGL_TEXTURE_2D_MULTISAMPLE, savedMSTexture1);
driver->glActiveTexture((GLenum)savedActiveTexture);
driver->glBindBuffer(eGL_SHADER_STORAGE_BUFFER, savedShaderStorageBuffer);
driver->glBindBuffer(eGL_UNIFORM_BUFFER, savedUniformBuffer);
}
rdcarray<EventUsage> QueryModifyingEvents(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &events, int x, int y,
int mipLevel, rdcarray<PixelModification> &history)
{
GLMarkerRegion region("QueryModifyingEvents");
rdcarray<EventUsage> modEvents;
rdcarray<GLuint> occlusionQueries;
std::set<uint32_t> ignoredEvents;
occlusionQueries.resize(events.size());
driver->glGenQueries((GLsizei)occlusionQueries.size(), occlusionQueries.data());
driver->ReplayLog(0, events[0].eventId, eReplay_WithoutDraw);
// execute the occlusion queries
for(size_t i = 0; i < events.size(); i++)
{
uint32_t colIdx = getFramebufferColIndex(driver, resources.target);
bool ignored = false;
int objectType;
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &objectType);
// Ignore the event if the framebuffer is attached to a different mip level than the one we're
// interested in
if(objectType == eGL_TEXTURE && events[i].usage != ResourceUsage::Clear &&
!isDirectWrite(events[i].usage))
{
int attachedMipLevel;
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, &attachedMipLevel);
if(mipLevel != attachedMipLevel)
{
ignoredEvents.insert(events[i].eventId);
ignored = true;
}
}
if(!ignored && !(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage)))
{
GLRenderState state;
state.FetchState(driver);
GLint currentProgram = 0;
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &currentProgram);
if(!IsGLES)
driver->glEnable(eGL_DEPTH_CLAMP);
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
driver->glDisable(eGL_DEPTH_TEST);
driver->glDisable(eGL_STENCIL_TEST);
driver->glDisable(eGL_CULL_FACE);
driver->glDisable(eGL_SAMPLE_MASK);
driver->glEnable(eGL_SCISSOR_TEST);
driver->glScissor(x, y, 1, 1);
driver->glUseProgram(GetFixedColProgram(driver, driver->GetReplay(), resources, currentProgram));
driver->SetFetchCounters(true);
driver->glBeginQuery(eGL_SAMPLES_PASSED, occlusionQueries[i]);
driver->ReplayLog(events[i].eventId, events[i].eventId, eReplay_OnlyDraw);
driver->glEndQuery(eGL_SAMPLES_PASSED);
driver->SetFetchCounters(false);
state.ApplyState(driver);
}
else
{
driver->ReplayLog(events[i].eventId, events[i].eventId, eReplay_OnlyDraw);
}
if(i < events.size() - 1)
{
driver->ReplayLog(events[i].eventId + 1, events[i + 1].eventId, eReplay_WithoutDraw);
}
}
// read back the occlusion queries and generate the list of potentially modifying events
for(size_t i = 0; i < events.size(); i++)
{
if(ignoredEvents.count(events[i].eventId) == 1)
{
continue;
}
if(events[i].usage == ResourceUsage::Clear || isDirectWrite(events[i].usage))
{
PixelModification mod;
RDCEraseEl(mod);
mod.eventId = events[i].eventId;
mod.directShaderWrite = isDirectWrite(events[i].usage);
history.push_back(mod);
modEvents.push_back(events[i]);
}
else
{
uint32_t numSamples;
driver->glGetQueryObjectuiv(occlusionQueries[i], eGL_QUERY_RESULT, &numSamples);
if(numSamples > 0)
{
PixelModification mod;
RDCEraseEl(mod);
mod.eventId = events[i].eventId;
history.push_back(mod);
modEvents.push_back(events[i]);
}
}
}
driver->glDeleteQueries((GLsizei)occlusionQueries.size(), occlusionQueries.data());
return modEvents;
}
struct ScopedReadPixelsSanitiser
{
PixelUnpackState unpack;
PixelPackState pack;
GLuint pixelPackBuffer = 0;
GLuint pixelUnpackBuffer = 0;
ScopedReadPixelsSanitiser()
{
unpack.Fetch(false);
pack.Fetch(false);
GL.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, (GLint *)&pixelPackBuffer);
GL.glGetIntegerv(eGL_PIXEL_UNPACK_BUFFER_BINDING, (GLint *)&pixelUnpackBuffer);
ResetPixelPackState(false, 1);
ResetPixelUnpackState(false, 1);
GL.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
GL.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, 0);
}
~ScopedReadPixelsSanitiser()
{
unpack.Apply(false);
pack.Apply(false);
GL.glBindBuffer(eGL_PIXEL_PACK_BUFFER, pixelPackBuffer);
GL.glBindBuffer(eGL_PIXEL_UNPACK_BUFFER, pixelUnpackBuffer);
}
};
void readPixelValues(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
const CopyFramebuffer &copyFramebuffer, rdcarray<PixelModification> &history,
int historyIndex, ModType modType, bool readStencil, uint32_t numPixels)
{
ScopedReadPixelsSanitiser scope;
GLenum colourFormatType = getTextureFormatType(copyFramebuffer.format.colorFormat);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copyFramebuffer.framebufferId);
rdcarray<uint32_t> intColourValues;
intColourValues.resize(4 * numPixels);
rdcarray<float> floatColourValues;
floatColourValues.resize(4 * numPixels);
rdcarray<float> depthValues;
depthValues.resize(numPixels);
rdcarray<int> stencilValues;
stencilValues.resize(numPixels);
if(colourFormatType == eGL_UNSIGNED_INT)
{
driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_RGBA_INTEGER, eGL_UNSIGNED_INT,
(void *)intColourValues.data());
}
else if(colourFormatType == eGL_UNSIGNED_INT)
{
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(IsSRGBFormat(copyFramebuffer.format.colorFormat))
for(float &f : floatColourValues)
f = ConvertSRGBToLinear(f);
}
if(copyFramebuffer.dsTextureId != 0 || copyFramebuffer.depthTextureId != 0)
{
driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_DEPTH_COMPONENT, eGL_FLOAT,
(void *)depthValues.data());
}
if(copyFramebuffer.dsTextureId != 0 || copyFramebuffer.stencilTextureId != 0)
{
driver->glReadPixels(0, 0, GLint(numPixels), 1, eGL_STENCIL_INDEX, eGL_INT,
(void *)stencilValues.data());
}
for(size_t i = 0; i < numPixels; i++)
{
ModificationValue modValue;
for(int j = 0; j < 4; ++j)
{
if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_UNSIGNED_INT)
{
modValue.col.uintValue[j] = intColourValues[i * 4 + j];
}
else
{
modValue.col.floatValue[j] = floatColourValues[i * 4 + j];
}
}
modValue.depth = depthValues[i];
if(readStencil)
{
modValue.stencil = stencilValues[i];
}
else
{
// if this isn't the last fragment in this event, the stencil is unavailable
if(historyIndex + i + 1 < history.size() &&
history[historyIndex + i].eventId == history[historyIndex + i + 1].eventId)
modValue.stencil = -2;
else
modValue.stencil = history[historyIndex + i].postMod.stencil;
}
if(modType == ModType::PreMod)
history[historyIndex + i].preMod = modValue;
else
history[historyIndex + i].postMod = modValue;
}
}
void readPixelValuesMS(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
const CopyFramebuffer &copyFramebuffer, int sampleIdx, int x, int y,
rdcarray<PixelModification> &history, size_t historyIndex, ModType modType,
bool readStencil)
{
rdcarray<float> pixelValue;
pixelValue.resize(8);
CopyMSSample(driver, resources, copyFramebuffer, sampleIdx, x, y, pixelValue.data());
const int depthOffset = 4;
const int stencilOffset = 5;
ModificationValue &modValue =
modType == ModType::PreMod ? history[historyIndex].preMod : history[historyIndex].postMod;
for(int j = 0; j < 4; ++j)
{
modValue.col.floatValue[j] = pixelValue[j];
}
modValue.depth = pixelValue[depthOffset];
if(readStencil)
{
modValue.stencil = *(int *)&pixelValue[stencilOffset];
}
}
void QueryPrePostModPixelValues(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents, int x, int y,
rdcarray<PixelModification> &history, uint32_t numSamples,
uint32_t sampleIndex)
{
GLMarkerRegion region("QueryPrePostModPixelValues");
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
CopyFramebuffer premodCopyFramebuffer = {};
premodCopyFramebuffer.framebufferId = ~0u;
CopyFramebuffer postmodCopyFramebuffer = {};
postmodCopyFramebuffer.framebufferId = ~0u;
int preModLastIdx = 0, postModLastIdx = 0;
for(size_t i = 0; i < modEvents.size(); i++)
{
uint32_t colIdx = getFramebufferColIndex(driver, resources.target);
{
GLint savedReadFramebuffer, savedDrawFramebuffer;
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
GLuint copySourceFramebuffer = savedDrawFramebuffer;
if(isDirectWrite(modEvents[i].usage))
{
copySourceFramebuffer = resources.copySourceFramebuffer;
// bind this as the framebuffer so the below code which expects to duplicate based on bound
// framebuffer will work. We've already saved teh real one
driver->glBindFramebuffer(eGL_FRAMEBUFFER, copySourceFramebuffer);
}
if(numSamples > 1)
{
premodCopyFramebuffer =
getCopyFramebuffer(driver, colIdx, resources.copyFramebuffers, ModType::PreMod,
numSamples, int(modEvents.size()));
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, premodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copySourceFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
readPixelValuesMS(driver, resources, premodCopyFramebuffer, sampleIndex, 0, 0, history, i,
ModType::PreMod, true);
driver->glReadBuffer(savedReadBuffer);
}
else
{
CopyFramebuffer newCopyFramebuffer =
getCopyFramebuffer(driver, colIdx, resources.copyFramebuffers, ModType::PreMod,
1 /*single sampled*/, int(modEvents.size()));
if(newCopyFramebuffer.framebufferId != premodCopyFramebuffer.framebufferId)
{
if(premodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, premodCopyFramebuffer, history, preModLastIdx,
ModType::PreMod, true, (uint32_t)(i - preModLastIdx));
}
preModLastIdx = int(i);
}
premodCopyFramebuffer = newCopyFramebuffer;
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, premodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copySourceFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(i) - preModLastIdx, 0,
GLint(i) + 1 - preModLastIdx, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
driver->glReadBuffer(savedReadBuffer);
}
// restore the capture's framebuffer
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
}
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
{
GLint savedReadFramebuffer, savedDrawFramebuffer;
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
GLuint copySourceFramebuffer = savedDrawFramebuffer;
if(isDirectWrite(modEvents[i].usage))
{
copySourceFramebuffer = resources.copySourceFramebuffer;
// bind this as the framebuffer so the below code which expects to duplicate based on bound
// framebuffer will work. We've already saved teh real one
driver->glBindFramebuffer(eGL_FRAMEBUFFER, copySourceFramebuffer);
}
if(numSamples > 1)
{
postmodCopyFramebuffer =
getCopyFramebuffer(driver, colIdx, resources.copyFramebuffers, ModType::PostMod,
numSamples, int(modEvents.size()));
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, postmodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copySourceFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
readPixelValuesMS(driver, resources, postmodCopyFramebuffer, sampleIndex, 0, 0, history, i,
ModType::PostMod, true);
driver->glReadBuffer(savedReadBuffer);
}
else
{
CopyFramebuffer newCopyFramebuffer =
getCopyFramebuffer(driver, colIdx, resources.copyFramebuffers, ModType::PostMod,
1 /*single sampled*/, int(modEvents.size()));
if(newCopyFramebuffer.framebufferId != postmodCopyFramebuffer.framebufferId)
{
if(postmodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, postmodCopyFramebuffer, history, postModLastIdx,
ModType::PostMod, true, (uint32_t)(i - postModLastIdx));
}
postModLastIdx = int(i);
}
postmodCopyFramebuffer = newCopyFramebuffer;
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, postmodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, copySourceFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(i) - postModLastIdx, 0,
GLint(i) + 1 - postModLastIdx, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
driver->glReadBuffer(savedReadBuffer);
}
// 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);
}
}
if(numSamples == 1 && premodCopyFramebuffer.framebufferId != 0u)
{
readPixelValues(driver, resources, premodCopyFramebuffer, history, preModLastIdx,
ModType::PreMod, true, int(modEvents.size()) - preModLastIdx);
}
if(numSamples == 1 && postmodCopyFramebuffer.framebufferId != 0u)
{
readPixelValues(driver, resources, postmodCopyFramebuffer, history, postModLastIdx,
ModType::PostMod, true, int(modEvents.size()) - postModLastIdx);
}
}
ModificationValue readShaderOutMS(WrappedOpenGL *driver, const GLPixelHistoryResources &resources,
const CopyFramebuffer &copyFramebuffer, int sampleIdx, int x, int y)
{
rdcarray<float> pixelValue;
pixelValue.resize(8);
CopyMSSample(driver, resources, copyFramebuffer, sampleIdx, x, y, pixelValue.data());
const int depthOffset = 4;
const int stencilOffset = 5;
ModificationValue modValue;
for(int j = 0; j < 4; ++j)
{
modValue.col.floatValue[j] = pixelValue[j];
}
modValue.depth = pixelValue[depthOffset];
modValue.stencil = *(int *)&pixelValue[stencilOffset];
return modValue;
}
struct EventFragmentData
{
std::map<uint32_t, uint32_t> eventFragments;
rdcarray<uint32_t> fragmentsClipped;
};
// This function gets:
// - calculates the number of fragments per event
// - per-event whether some fragments were discarded
// - reads the shader output values per event (per-fragment shader output is fetched later)
EventFragmentData QueryNumFragmentsByEvent(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents,
rdcarray<PixelModification> &history, int x, int y,
uint32_t numSamples, uint32_t sampleIndex,
uint32_t width, uint32_t height)
{
GLMarkerRegion region("QueryNumFragmentsByEvent");
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
EventFragmentData ret;
std::map<uint32_t, uint32_t> &eventFragments = ret.eventFragments;
rdcarray<uint32_t> &fragmentsClipped = ret.fragmentsClipped;
for(size_t i = 0; i < modEvents.size(); ++i)
{
GLRenderState state;
state.FetchState(driver);
uint32_t colIdx = getFramebufferColIndex(driver, resources.target);
GLenum colourFormat = getCurrentTextureFormat(driver, colIdx);
GLenum colourFormatType = getTextureFormatType(colourFormat);
GLenum shaderOutColourFormat = eGL_NONE;
if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT)
{
shaderOutColourFormat = colourFormatType == eGL_UNSIGNED_INT ? eGL_RGBA32UI : eGL_RGBA32I;
FramebufferKey key = {ModType::PostMod, shaderOutColourFormat, eGL_DEPTH32F_STENCIL8,
eGL_DEPTH32F_STENCIL8, numSamples};
ShaderOutFramebuffer framebuffer =
getShaderOutFramebuffer(driver, colIdx, resources, key, width, height);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, framebuffer.framebufferId);
glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
}
else
{
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
for(uint32_t a = 0; a < 8; a++)
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + a), 0, 0);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
resources.fullPrecisionColorImage, 0);
glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
}
rdcarray<GLenum> drawBufs;
drawBufs.resize(colIdx);
drawBufs.push_back(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
driver->glDrawBuffers((GLsizei)drawBufs.size(), drawBufs.data());
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->glClearColor(0, 0, 0, 0);
if(driver->isGLESMode())
{
driver->glClearDepthf(0.0f);
}
else
{
driver->glClearDepth(0);
}
driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT);
driver->glEnable(eGL_STENCIL_TEST);
// depth test enable
driver->glEnable(eGL_DEPTH_TEST);
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
driver->glDepthFunc(eGL_ALWAYS);
driver->glDepthMask(GL_TRUE);
driver->glDisable(eGL_BLEND);
driver->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// enable the sample we're looking at so we get the shaderOut even if it's masked off
driver->glEnable(eGL_SAMPLE_MASK);
driver->glSampleMaski(0, 1u << sampleIndex);
// replay event
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
uint32_t numFragments = 0;
uint32_t numFragmentsWithoutDiscard = 0;
if(modEvents[i].usage != ResourceUsage::Clear && !isDirectWrite(modEvents[i].usage))
{
if(numSamples == 1)
{
ScopedReadPixelsSanitiser scope;
ModificationValue modValue;
if(colourFormatType == eGL_UNSIGNED_INT)
{
driver->glReadPixels(x, y, 1, 1, eGL_RGBA_INTEGER, eGL_UNSIGNED_INT,
(void *)modValue.col.uintValue.data());
}
else if(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);
// 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;
if(resources.depthTarget)
RDCEraseEl(history[i].shaderOut.col);
}
else
{
GLenum copyFramebufferColourFormat =
(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT)
? shaderOutColourFormat
: eGL_RGBA32F;
const CopyFramebuffer &copyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, copyFramebufferColourFormat);
GLint savedDrawFramebuffer;
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
glReadBuffer(eGL_COLOR_ATTACHMENT0);
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
driver->glReadBuffer(savedReadBuffer);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
history[i].shaderOut = readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0);
numFragments = history[i].shaderOut.stencil;
history[i].shaderOut.stencil = history[i].postMod.stencil;
if(resources.depthTarget)
RDCEraseEl(history[i].shaderOut.col);
}
GLint currentProgram = 0;
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &currentProgram);
// re-run draw with program that outputs a fixed colour to count how many fragments we get without discards
driver->glUseProgram(GetFixedColProgram(driver, driver->GetReplay(), resources, currentProgram));
driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT);
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
if(numSamples == 1)
{
driver->glReadPixels(x, y, 1, 1, eGL_STENCIL_INDEX, eGL_UNSIGNED_INT,
(void *)&numFragmentsWithoutDiscard);
}
else
{
GLenum copyFramebufferColourFormat =
(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT)
? shaderOutColourFormat
: eGL_RGBA32F;
const CopyFramebuffer &copyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, copyFramebufferColourFormat);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
glReadBuffer(eGL_COLOR_ATTACHMENT0);
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
numFragmentsWithoutDiscard =
readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0).stencil;
}
}
eventFragments.emplace(modEvents[i].eventId, numFragmentsWithoutDiscard);
if(numFragmentsWithoutDiscard > numFragments)
fragmentsClipped.push_back(modEvents[i].eventId);
state.ApplyState(driver);
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
}
return ret;
}
bool QueryScissorTest(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const EventUsage event, int x, int y)
{
driver->ReplayLog(0, event.eventId, eReplay_WithoutDraw);
bool scissorTestFailed = false;
if(driver->glIsEnabled(eGL_SCISSOR_TEST))
{
int scissorBox[4]; // [x, y, width, height]
driver->glGetIntegerv(eGL_SCISSOR_BOX, scissorBox);
scissorTestFailed = x < scissorBox[0] || x - scissorBox[0] >= scissorBox[2] ||
y < scissorBox[1] || y - scissorBox[1] >= scissorBox[3];
}
return scissorTestFailed;
}
bool QueryTest(WrappedOpenGL *driver, GLPixelHistoryResources &resources, const EventUsage event,
int x, int y, OpenGLTest test, uint32_t sampleIndex)
{
driver->ReplayLog(0, event.eventId, eReplay_WithoutDraw);
GLuint samplesPassedQuery;
driver->glGenQueries(1, &samplesPassedQuery);
driver->glEnable(eGL_SCISSOR_TEST);
driver->glScissor(x, y, 1, 1);
if(test < OpenGLTest::DepthTest)
{
driver->glDisable(eGL_DEPTH_TEST);
}
if(test < OpenGLTest::DepthClamp)
{
if(!IsGLES)
driver->glEnable(eGL_DEPTH_CLAMP);
}
if(test < OpenGLTest::DepthBounds)
{
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
}
if(test < OpenGLTest::Discard)
{
GLint currentProgram = 0;
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &currentProgram);
driver->glUseProgram(GetFixedColProgram(driver, driver->GetReplay(), resources, currentProgram));
}
if(test < OpenGLTest::StencilTest)
{
driver->glDisable(eGL_STENCIL_TEST);
}
if(test < OpenGLTest::FaceCulling)
{
driver->glDisable(eGL_CULL_FACE);
}
if(test < OpenGLTest::SampleMask)
{
driver->glDisable(eGL_SAMPLE_MASK);
}
else if(test == OpenGLTest::SampleMask)
{
GLboolean sampleMaskEnabled = driver->glIsEnabled(eGL_SAMPLE_MASK);
if(sampleMaskEnabled)
{
uint32_t currentSampleMask;
driver->glGetIntegeri_v(eGL_SAMPLE_MASK_VALUE, 0, (GLint *)&currentSampleMask);
uint32_t newSampleMask = currentSampleMask & (sampleIndex == ~0u ? ~0u : (1u << sampleIndex));
driver->glSampleMaski(0, newSampleMask);
}
else
{
driver->glEnable(eGL_SAMPLE_MASK);
driver->glSampleMaski(0, 1u << sampleIndex);
}
}
driver->SetFetchCounters(true);
driver->glBeginQuery(eGL_ANY_SAMPLES_PASSED, samplesPassedQuery);
driver->ReplayLog(event.eventId, event.eventId, eReplay_OnlyDraw);
driver->glEndQuery(eGL_ANY_SAMPLES_PASSED);
driver->SetFetchCounters(false);
int anySamplesPassed = GL_FALSE;
driver->glGetQueryObjectiv(samplesPassedQuery, eGL_QUERY_RESULT, &anySamplesPassed);
driver->glDeleteQueries(1, &samplesPassedQuery);
return anySamplesPassed == GL_FALSE;
}
void QueryFailedTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents, int x, int y,
rdcarray<PixelModification> &history, uint32_t sampleIndex)
{
GLMarkerRegion region("QueryFailedTests");
for(size_t i = 0; i < modEvents.size(); ++i)
{
OpenGLTest failedTest = OpenGLTest::NumTests;
if(!isDirectWrite(modEvents[i].usage))
{
if(modEvents[i].usage == ResourceUsage::Clear)
{
bool failed = QueryScissorTest(driver, resources, modEvents[i], x, y);
if(failed)
{
failedTest = OpenGLTest::ScissorTest;
}
}
else
{
for(int test = 0; test < int(OpenGLTest::NumTests); ++test)
{
bool failed;
if(test == int(OpenGLTest::ScissorTest))
{
failed = QueryScissorTest(driver, resources, modEvents[i], x, y);
}
else
{
failed = QueryTest(driver, resources, modEvents[i], x, y, OpenGLTest(test), sampleIndex);
}
if(failed)
{
failedTest = OpenGLTest(test);
break;
}
}
}
}
// 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].depthClipped = failedTest == OpenGLTest::DepthClamp;
history[i].depthBoundsFailed = failedTest == OpenGLTest::DepthBounds;
history[i].stencilTestFailed = failedTest == OpenGLTest::StencilTest;
history[i].depthTestFailed = failedTest == OpenGLTest::DepthTest;
history[i].shaderDiscarded = failedTest == OpenGLTest::Discard;
history[i].backfaceCulled = failedTest == OpenGLTest::FaceCulling;
history[i].sampleMasked = failedTest == OpenGLTest::SampleMask;
}
}
void QueryShaderOutPerFragment(WrappedOpenGL *driver, GLReplay *replay,
GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents, int x, int y,
rdcarray<PixelModification> &history,
const std::map<uint32_t, uint32_t> &eventFragments,
uint32_t numSamples, uint32_t sampleIndex, uint32_t width,
uint32_t height)
{
GLMarkerRegion region("QueryShaderOutPerFragment");
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
for(size_t i = 0; i < modEvents.size(); ++i)
{
auto it = eventFragments.find(modEvents[i].eventId);
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
if(numFragments <= 1)
{
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
continue;
}
GLRenderState state;
state.FetchState(driver);
driver->glEnable(eGL_SCISSOR_TEST);
driver->glScissor(x, y, 1, 1);
driver->glClearStencil(0);
driver->glClearColor(0, 0, 0, 0);
driver->glClearDepth(0);
driver->glClearStencil(0);
driver->glEnable(eGL_DEPTH_TEST);
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
driver->glDepthFunc(eGL_ALWAYS);
driver->glDepthMask(GL_TRUE);
driver->glDisable(eGL_BLEND);
driver->glEnable(eGL_SAMPLE_MASK);
if(sampleIndex != ~0u)
{
driver->glSampleMaski(0, 1u << sampleIndex);
}
else
{
driver->glSampleMaski(0, ~0u);
}
driver->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
driver->glStencilMask(0xff);
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
driver->glEnable(eGL_STENCIL_TEST);
// enable the sample we're looking at so we get the shaderOut even if it's masked off
if(sampleIndex != ~0u)
{
driver->glSampleMaski(0, 1u << sampleIndex);
}
PixelModification referenceHistory;
referenceHistory.eventId = modEvents[i].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());
uint32_t colIdx = getFramebufferColIndex(driver, resources.target);
GLenum colourFormat = getCurrentTextureFormat(driver, colIdx);
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);
if(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT)
{
shaderOutColourFormat = colourFormatType == eGL_UNSIGNED_INT ? eGL_RGBA32UI : eGL_RGBA32I;
FramebufferKey key = {ModType::PostMod, shaderOutColourFormat, eGL_DEPTH32F_STENCIL8,
eGL_DEPTH32F_STENCIL8, numSamples};
ShaderOutFramebuffer framebuffer =
getShaderOutFramebuffer(driver, colIdx, resources, key, width, height);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, framebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, framebuffer.framebufferId);
glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
}
else
{
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
for(uint32_t a = 0; a < 8; a++)
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + a), 0, 0);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
resources.fullPrecisionColorImage, 0);
glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
}
rdcarray<GLenum> drawBufs;
drawBufs.resize(colIdx);
drawBufs.push_back(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
driver->glDrawBuffers((GLsizei)drawBufs.size(), drawBufs.data());
driver->glClear(eGL_STENCIL_BUFFER_BIT | eGL_COLOR_BUFFER_BIT | eGL_DEPTH_BUFFER_BIT);
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
if(numSamples == 1)
{
ScopedReadPixelsSanitiser scope;
ModificationValue modValue;
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;
historyIndex->shaderOut = modValue;
}
else
{
GLenum copyFramebufferColourFormat =
(colourFormatType == eGL_UNSIGNED_INT || colourFormatType == eGL_INT)
? shaderOutColourFormat
: eGL_RGBA32F;
const CopyFramebuffer &copyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, copyFramebufferColourFormat);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
glReadBuffer(eGL_COLOR_ATTACHMENT0);
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
int oldStencil = historyIndex->shaderOut.stencil;
historyIndex->shaderOut =
readShaderOutMS(driver, resources, copyFramebuffer, sampleIndex, 0, 0);
historyIndex->shaderOut.stencil = oldStencil;
}
historyIndex++;
}
state.ApplyState(driver);
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
}
}
void QueryPrePostModPerFragment(WrappedOpenGL *driver, GLReplay *replay,
GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents, int x, int y,
rdcarray<PixelModification> &history,
const EventFragmentData &eventFragmentData, uint32_t numSamples,
uint32_t sampleIndex, uint32_t width, uint32_t height)
{
const std::map<uint32_t, uint32_t> &eventFragments = eventFragmentData.eventFragments;
GLMarkerRegion region("QueryPrePostModPerFragment");
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
for(size_t i = 0; i < modEvents.size(); i++)
{
auto it = eventFragments.find(modEvents[i].eventId);
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
if(numFragments <= 1)
{
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
continue;
}
GLRenderState state;
state.FetchState(driver);
driver->glEnable(eGL_SCISSOR_TEST);
driver->glScissor(x, y, 1, 1);
driver->glClearStencil(0);
driver->glStencilMask(0xff);
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
driver->glEnable(eGL_STENCIL_TEST);
driver->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
PixelModification referenceHistory;
referenceHistory.eventId = modEvents[i].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());
GLint savedReadFramebuffer, savedDrawFramebuffer;
driver->glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, &savedDrawFramebuffer);
driver->glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &savedReadFramebuffer);
uint32_t colIdx = getFramebufferColIndex(driver, resources.target);
CopyFramebuffer postmodCopyFramebuffer = {};
postmodCopyFramebuffer.framebufferId = ~0u;
CopyFramebuffer premodCopyFramebuffer = {};
premodCopyFramebuffer.framebufferId = ~0u;
int preModLastJ = 0;
int postModLastJ = 0;
// save D/S attachment since we'll substitute our own depth
GLuint curDepth;
GLint curDepthType;
GLuint curStencil;
GLint curStencilType;
GLint curStencilLevel = 0, curDepthLevel = 0;
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
&curDepthType);
if(curDepth)
{
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
&curDepthLevel);
}
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curStencil);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
&curStencilType);
if(curStencil)
{
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL,
&curStencilLevel);
}
// if we didn't have a depth buffer, turn off depth testing so we don't have to worry about
// incorrect depth failures with our forced depth
if(!curDepth)
{
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
driver->glDepthFunc(eGL_ALWAYS);
}
GLuint forcedDepth = 0;
// replace depth and clear it to premod value
{
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
driver->glFramebufferTexture(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, forcedDepth,
0);
GLenum textureTarget = numSamples > 1 ? eGL_TEXTURE_2D_MULTISAMPLE : eGL_TEXTURE_2D;
driver->glGenTextures(1, &forcedDepth);
driver->CreateTextureImage(forcedDepth, eGL_DEPTH32F_STENCIL8, eGL_NONE, eGL_NONE,
textureTarget, 2, width, height, 1, numSamples, 1);
driver->glFramebufferTexture(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_STENCIL_ATTACHMENT, forcedDepth,
0);
driver->glClearDepth(historyIndex->preMod.depth);
driver->glClear(eGL_DEPTH_BUFFER_BIT);
}
GLuint curColor = 0;
GLint colorType = 0;
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, (GLint *)&curColor);
driver->glGetFramebufferAttachmentParameteriv(
eGL_DRAW_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + colIdx),
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));
}
colorFormat = driver->m_Textures[id].internalFormat;
}
for(size_t j = 0; j < std::max(numFragments, 1u); ++j)
{
// Set the stencil function so only jth fragment will pass.
driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff);
driver->glClear(eGL_STENCIL_BUFFER_BIT);
if(j > 0)
{
if(numSamples > 1)
{
premodCopyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PreMod, numSamples,
int(modEvents.size()), eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, colorFormat);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, premodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
readPixelValuesMS(driver, resources, premodCopyFramebuffer, sampleIndex, 0, 0, history,
(historyIndex - history.begin()), ModType::PreMod, false);
driver->glReadBuffer(savedReadBuffer);
}
else
{
// Blit the values into out framebuffer
CopyFramebuffer newCopyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PreMod, numSamples,
int(modEvents.size()), eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, colorFormat);
if(newCopyFramebuffer.framebufferId != premodCopyFramebuffer.framebufferId ||
(j - preModLastJ >= premodCopyFramebuffer.width))
{
if(premodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, premodCopyFramebuffer, history,
preModLastJ + int(historyIndex - history.begin()), ModType::PreMod,
false, (uint32_t)(j - preModLastJ));
}
preModLastJ = int(j);
}
premodCopyFramebuffer = newCopyFramebuffer;
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, premodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(j) - preModLastJ, 0,
GLint(j) + 1 - preModLastJ, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
driver->glReadBuffer(savedReadBuffer);
}
}
// restore the capture's framebuffer
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
if(numSamples > 1)
{
postmodCopyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, colorFormat);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, postmodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
readPixelValuesMS(driver, resources, postmodCopyFramebuffer, sampleIndex, 0, 0, history,
(historyIndex - history.begin()), ModType::PostMod, false);
historyIndex++;
driver->glReadBuffer(savedReadBuffer);
}
else
{
// Blit the values into out framebuffer
CopyFramebuffer newCopyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, colorFormat);
if(newCopyFramebuffer.framebufferId != postmodCopyFramebuffer.framebufferId ||
(j - postModLastJ >= postmodCopyFramebuffer.width))
{
if(postmodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, postmodCopyFramebuffer, history,
postModLastJ + int(historyIndex - history.begin()), ModType::PostMod,
false, (uint32_t)(j - postModLastJ));
}
postModLastJ = int(j);
}
postmodCopyFramebuffer = newCopyFramebuffer;
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, postmodCopyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedDrawFramebuffer);
GLenum savedReadBuffer;
driver->glGetIntegerv(eGL_READ_BUFFER, (GLint *)&savedReadBuffer);
driver->glReadBuffer(GLenum(eGL_COLOR_ATTACHMENT0 + colIdx));
SafeBlitFramebuffer(x, y, x + 1, y + 1, GLint(j) - postModLastJ, 0,
GLint(j) + 1 - postModLastJ, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
driver->glReadBuffer(savedReadBuffer);
}
// restore the capture's framebuffer
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, savedDrawFramebuffer);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, savedReadFramebuffer);
}
// restore original D/S attachments
if(curDepthType != eGL_RENDERBUFFER)
{
driver->glFramebufferTexture(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT, curDepth,
curDepthLevel);
}
else
{
driver->glFramebufferRenderbuffer(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_RENDERBUFFER, curDepth);
}
if(curStencilType != eGL_RENDERBUFFER)
{
driver->glFramebufferTexture(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT, curStencil,
curStencilLevel);
}
else
{
driver->glFramebufferRenderbuffer(eGL_DRAW_FRAMEBUFFER, eGL_STENCIL_ATTACHMENT,
eGL_RENDERBUFFER, curStencil);
}
driver->glDeleteTextures(1, &forcedDepth);
if(numSamples == 1 && premodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, premodCopyFramebuffer, history,
preModLastJ + int(historyIndex - history.begin()), ModType::PreMod, false,
numFragments - preModLastJ);
}
if(numSamples == 1 && postmodCopyFramebuffer.framebufferId != ~0u)
{
readPixelValues(driver, resources, postmodCopyFramebuffer, history,
postModLastJ + int(historyIndex - history.begin()), ModType::PostMod, false,
numFragments - postModLastJ);
}
state.ApplyState(driver);
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
}
}
void QueryPrimitiveIdPerFragment(WrappedOpenGL *driver, GLReplay *replay,
GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents, int x, int y,
rdcarray<PixelModification> &history,
const std::map<uint32_t, uint32_t> &eventFragments,
bool usingFloatForPrimitiveId, uint32_t numSamples,
uint32_t sampleIndex)
{
GLMarkerRegion region("QueryPrimitiveIdPerFragment");
driver->ReplayLog(0, modEvents[0].eventId, eReplay_WithoutDraw);
for(size_t i = 0; i < modEvents.size(); i++)
{
auto it = eventFragments.find(modEvents[i].eventId);
uint32_t numFragments = (it != eventFragments.end()) ? it->second : 0;
if(numFragments == 0)
{
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
continue;
}
GLRenderState state;
state.FetchState(driver);
ResourceId ps;
if(state.Program.name)
ps = driver->GetProgram(driver->GetResourceManager()->GetResID(state.Program))
.stageShaders[(int)ShaderStage::Pixel];
else
ps = driver->GetPipeline(driver->GetResourceManager()->GetResID(state.Pipeline))
.stageShaders[(int)ShaderStage::Pixel];
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
driver->glReadBuffer(eGL_COLOR_ATTACHMENT0);
// rebind colour image to 0
for(uint32_t a = 1; a < 8; a++)
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + a), 0, 0);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0,
resources.fullPrecisionColorImage, 0);
driver->glDrawBuffer(eGL_COLOR_ATTACHMENT0);
driver->glEnable(eGL_SCISSOR_TEST);
driver->glScissor(x, y, 1, 1);
driver->glClearStencil(0);
driver->glEnable(eGL_DEPTH_TEST);
if(HasExt[EXT_depth_bounds_test])
driver->glDisable(eGL_DEPTH_BOUNDS_TEST_EXT);
driver->glDepthFunc(eGL_ALWAYS);
driver->glDepthMask(GL_TRUE);
driver->glDisable(eGL_BLEND);
driver->glDisable(eGL_SAMPLE_MASK);
driver->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
driver->glStencilMask(0xff);
driver->glStencilOp(eGL_INCR, eGL_INCR, eGL_INCR);
driver->glEnable(eGL_STENCIL_TEST);
PixelModification referenceHistory;
referenceHistory.eventId = modEvents[i].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());
// 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 = 0;
driver->glGetIntegerv(eGL_CURRENT_PROGRAM, &currentProgram);
driver->glUseProgram(GetPrimitiveIdProgram(driver, replay, resources, currentProgram));
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
// rebind output colour to 0
for(uint32_t a = 1; a < 8; a++)
driver->glFramebufferTexture(eGL_FRAMEBUFFER, GLenum(eGL_COLOR_ATTACHMENT0 + a), 0, 0);
driver->glFramebufferTexture(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0,
resources.fullPrecisionColorImage, 0);
driver->glDrawBuffer(eGL_COLOR_ATTACHMENT0);
driver->glReadBuffer(eGL_COLOR_ATTACHMENT0);
for(size_t j = 0; j < std::max(numFragments, 1u); ++j)
{
ModificationValue modValue;
modValue.stencil = 0;
// Set the stencil function so only jth fragment will pass.
driver->glBindFramebuffer(eGL_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
driver->glStencilFunc(eGL_EQUAL, (int)j, 0xff);
driver->glClear(eGL_STENCIL_BUFFER_BIT);
driver->ReplayLog(modEvents[i].eventId, modEvents[i].eventId, eReplay_OnlyDraw);
// Primitive id array is given size 8 for the multisampled case
float primitiveIds[8];
if(numSamples == 1)
{
ScopedReadPixelsSanitiser scope;
driver->glReadPixels(x, y, 1, 1, eGL_RGBA, eGL_FLOAT, (void *)primitiveIds);
}
else
{
const CopyFramebuffer &copyFramebuffer = getCopyFramebuffer(
driver, resources.copyFramebuffers, ModType::PostMod, numSamples, int(modEvents.size()),
eGL_DEPTH32F_STENCIL8, eGL_DEPTH32F_STENCIL8, eGL_RGBA32F);
driver->glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, copyFramebuffer.framebufferId);
driver->glBindFramebuffer(eGL_READ_FRAMEBUFFER, resources.fullPrecisionFrameBuffer);
glReadBuffer(eGL_COLOR_ATTACHMENT0);
SafeBlitFramebuffer(x, y, x + 1, y + 1, 0, 0, 1, 1, getFramebufferCopyMask(driver),
eGL_NEAREST);
CopyMSSample(driver, resources, copyFramebuffer, sampleIndex, 0, 0, primitiveIds);
}
if(primitiveIds[0] == primitiveIds[1] && primitiveIds[0] == primitiveIds[2] &&
primitiveIds[0] == primitiveIds[3])
{
if(usingFloatForPrimitiveId)
{
historyIndex->primitiveID = int(primitiveIds[0]);
}
else
{
historyIndex->primitiveID = *(int *)primitiveIds;
}
}
else
{
historyIndex->primitiveID = ~0u;
RDCERR("Primitive ID was not written correctly");
}
if(ps == ResourceId())
historyIndex->unboundPS = true;
++historyIndex;
}
driver->glUseProgram(currentProgram);
state.ApplyState(driver);
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId + 1, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
}
}
void CalculateFragmentDepthTests(WrappedOpenGL *driver, GLPixelHistoryResources &resources,
const rdcarray<EventUsage> &modEvents,
rdcarray<PixelModification> &history,
const std::map<uint32_t, uint32_t> &eventFragments)
{
GLMarkerRegion region("CalculateFragmentDepthTests");
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)
{
// keep the historyIndex in sync with the event index
while(historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId)
{
++historyIndex;
}
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
continue;
}
GLboolean depthTestEnabled = driver->glIsEnabled(eGL_DEPTH_TEST);
// default for no depth test
GLenum depthFunc = eGL_ALWAYS;
if(depthTestEnabled)
driver->glGetIntegerv(eGL_DEPTH_FUNC, (int *)&depthFunc);
for(; historyIndex < history.size() && modEvents[i].eventId == history[historyIndex].eventId;
++historyIndex)
{
if(historyIndex == 0)
{
continue;
}
GLuint curDepth = 0;
GLint depthType = 0;
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
(GLint *)&curDepth);
driver->glGetFramebufferAttachmentParameteriv(eGL_DRAW_FRAMEBUFFER, eGL_DEPTH_ATTACHMENT,
eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
&depthType);
GLenum depthFormat = eGL_NONE;
if(curDepth != 0)
{
ResourceId id;
if(depthType != eGL_RENDERBUFFER)
{
id = driver->GetResourceManager()->GetResID(TextureRes(driver->GetCtx(), curDepth));
}
else
{
id = driver->GetResourceManager()->GetResID(RenderbufferRes(driver->GetCtx(), curDepth));
}
depthFormat = driver->m_Textures[id].internalFormat;
uint32_t depthBits = 32;
if(depthFormat == eGL_DEPTH_COMPONENT24 || depthFormat == eGL_DEPTH24_STENCIL8 ||
depthFormat == eGL_UNSIGNED_INT_24_8)
{
depthBits = 24;
}
else if(depthFormat == eGL_DEPTH_COMPONENT16)
{
depthBits = 16;
}
history[historyIndex].CheckDepthTestQuantised(depthBits, MakeCompareFunc(depthFunc));
if(HasExt[EXT_depth_bounds_test] && GL.glIsEnabled(eGL_DEPTH_BOUNDS_TEST_EXT))
{
GLdouble depthBounds[2] = {};
GL.glGetDoublev(eGL_DEPTH_BOUNDS_EXT, depthBounds);
if((history[historyIndex].preMod.depth < depthBounds[0] ||
history[historyIndex].preMod.depth > depthBounds[1]) &&
depthBounds[1] > depthBounds[0])
{
history[historyIndex].depthBoundsFailed = true;
}
}
}
}
if(i < modEvents.size() - 1)
{
driver->ReplayLog(modEvents[i].eventId, modEvents[i + 1].eventId, eReplay_WithoutDraw);
}
}
}
}; // end of anonymous namespace
rdcarray<PixelModification> GLReplay::PixelHistory(rdcarray<EventUsage> events, ResourceId target,
uint32_t x, uint32_t y, const Subresource &sub,
CompType typeCast)
{
rdcarray<PixelModification> history;
if(events.empty())
return history;
TextureDescription textureDesc = GetTexture(target);
if(textureDesc.format.type == ResourceFormatType::Undefined)
return history;
// When RenderDoc passed y, the value being passed in is with the Y axis starting from the top
// However, we need to have it starting from the bottom, so flip it by subtracting y from the
// height.
uint32_t flippedY = (textureDesc.height >> sub.mip) - y - 1;
rdcstr regionName = StringFormat::Fmt(
"PixelHistory: pixel: (%u, %u) on %s subresource (%u, %u, %u) cast to %s with %zu events", x,
flippedY, ToStr(target).c_str(), sub.mip, sub.slice, sub.sample, ToStr(typeCast).c_str(),
events.size());
uint32_t sampleIdx = sub.sample;
if(sampleIdx > textureDesc.msSamp)
{
sampleIdx = 0; // if the sample index is out of range (ie. average value) then default to 0
}
uint32_t sampleMask = ~0U;
if(sampleIdx < 32)
{
sampleMask = 1U << sampleIdx;
}
GLPixelHistoryResources resources;
resources.target = target;
MakeCurrentReplayContext(&m_ReplayCtx);
m_pDriver->ReplayMarkers(false);
GLMarkerRegion region(regionName);
int glslVersion = DebugData.glslVersion;
bool usingFloatForPrimitiveId = glslVersion < 330;
PixelHistorySetupResources(m_pDriver, resources, textureDesc, sub, (uint32_t)events.size(),
glslVersion, textureDesc.msSamp);
rdcarray<EventUsage> modEvents =
QueryModifyingEvents(m_pDriver, resources, events, x, flippedY, sub.mip, history);
if(modEvents.empty())
{
PixelHistoryDestroyResources(m_pDriver, resources);
m_pDriver->ReplayMarkers(true);
return history;
}
QueryFailedTests(m_pDriver, resources, modEvents, x, flippedY, history, sampleIdx);
QueryPrePostModPixelValues(m_pDriver, resources, modEvents, x, flippedY, history,
textureDesc.msSamp, sampleIdx);
uint32_t textureWidth = textureDesc.width >> sub.mip;
uint32_t textureHeight = textureDesc.height >> sub.mip;
EventFragmentData eventFragmentData =
QueryNumFragmentsByEvent(m_pDriver, resources, modEvents, history, x, flippedY,
textureDesc.msSamp, sampleIdx, textureWidth, textureHeight);
std::map<uint32_t, uint32_t> &eventFragments = eventFragmentData.eventFragments;
// 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);
}
QueryShaderOutPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
eventFragments, textureDesc.msSamp, sampleIdx, textureWidth,
textureHeight);
QueryPrimitiveIdPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
eventFragments, usingFloatForPrimitiveId, textureDesc.msSamp,
sampleIdx);
QueryPrePostModPerFragment(m_pDriver, this, resources, modEvents, x, flippedY, history,
eventFragmentData, textureDesc.msSamp, sampleIdx, textureWidth,
textureHeight);
CalculateFragmentDepthTests(m_pDriver, resources, modEvents, history, eventFragments);
PixelHistoryDestroyResources(m_pDriver, resources);
m_pDriver->ReplayMarkers(true);
return history;
}