Files
renderdoc/renderdoc/driver/gl/gl_rendertexture.cpp
T
Mikkel Gjoel 41d39b58ce Pass range-min/max to ApplyCustomShader
Adding rangeminmax to globals, snippet support and updated docs to match

Fixes bug in replay_output.cpp, causing a crash due to missing texhandle
2021-11-03 20:26:18 +00:00

706 lines
24 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2021 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 "maths/formatpacking.h"
#include "maths/matrix.h"
#include "gl_driver.h"
#include "gl_replay.h"
#include "gl_resources.h"
#define OPENGL 1
#include "data/glsl/glsl_ubos_cpp.h"
bool GLReplay::RenderTexture(TextureDisplay cfg)
{
return RenderTextureInternal(cfg, TexDisplayFlags(eTexDisplay_BlendAlpha | eTexDisplay_MipShift));
}
bool GLReplay::RenderTextureInternal(TextureDisplay cfg, TexDisplayFlags flags)
{
const bool blendAlpha = (flags & eTexDisplay_BlendAlpha) != 0;
const bool mipShift = (flags & eTexDisplay_MipShift) != 0;
WrappedOpenGL &drv = *m_pDriver;
auto &texDetails = m_pDriver->m_Textures[cfg.resourceId];
if(texDetails.internalFormat == eGL_NONE)
return false;
CacheTexture(cfg.resourceId);
bool renderbuffer = false;
int intIdx = 0;
int resType;
switch(texDetails.curType)
{
case eGL_RENDERBUFFER:
resType = RESTYPE_TEX2D;
if(texDetails.samples > 1)
resType = RESTYPE_TEX2DMS;
renderbuffer = true;
break;
case eGL_TEXTURE_1D: resType = RESTYPE_TEX1D; break;
default: RDCWARN("Unexpected texture type"); DELIBERATE_FALLTHROUGH();
case eGL_TEXTURE_2D: resType = RESTYPE_TEX2D; break;
case eGL_TEXTURE_2D_MULTISAMPLE: resType = RESTYPE_TEX2DMS; break;
case eGL_TEXTURE_2D_MULTISAMPLE_ARRAY: resType = RESTYPE_TEX2DMSARRAY; break;
case eGL_TEXTURE_RECTANGLE: resType = RESTYPE_TEXRECT; break;
case eGL_TEXTURE_BUFFER: resType = RESTYPE_TEXBUFFER; break;
case eGL_TEXTURE_3D: resType = RESTYPE_TEX3D; break;
case eGL_TEXTURE_CUBE_MAP: resType = RESTYPE_TEXCUBE; break;
case eGL_TEXTURE_1D_ARRAY: resType = RESTYPE_TEX1DARRAY; break;
case eGL_TEXTURE_2D_ARRAY: resType = RESTYPE_TEX2DARRAY; break;
case eGL_TEXTURE_CUBE_MAP_ARRAY: resType = RESTYPE_TEXCUBEARRAY; break;
}
GLuint texname = texDetails.resource.name;
GLenum target = texDetails.curType;
// do blit from renderbuffer to texture, then sample from texture
if(renderbuffer)
{
// need replay context active to do blit (as FBOs aren't shared)
MakeCurrentReplayContext(&m_ReplayCtx);
GLMarkerRegion blitRegion("Renderbuffer Blit");
GLuint curDrawFBO = 0;
GLuint curReadFBO = 0;
drv.glGetIntegerv(eGL_DRAW_FRAMEBUFFER_BINDING, (GLint *)&curDrawFBO);
drv.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, (GLint *)&curReadFBO);
drv.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, texDetails.renderbufferFBOs[1]);
drv.glBindFramebuffer(eGL_READ_FRAMEBUFFER, texDetails.renderbufferFBOs[0]);
SafeBlitFramebuffer(
0, 0, texDetails.width, texDetails.height, 0, 0, texDetails.width, texDetails.height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, eGL_NEAREST);
drv.glBindFramebuffer(eGL_DRAW_FRAMEBUFFER, curDrawFBO);
drv.glBindFramebuffer(eGL_READ_FRAMEBUFFER, curReadFBO);
texname = texDetails.renderbufferReadTex;
if(resType == RESTYPE_TEX2D)
target = eGL_TEXTURE_2D;
else
target = eGL_TEXTURE_2D_MULTISAMPLE;
}
MakeCurrentReplayContext(m_DebugCtx);
GLMarkerRegion region("RenderTextureInternal");
uint32_t numMips = m_CachedTextures[cfg.resourceId].mips;
GLuint castTexture = 0;
GLenum displayFormat = texDetails.internalFormat;
if(cfg.typeCast != CompType::Typeless &&
cfg.typeCast != MakeResourceFormat(target, displayFormat).compType)
{
displayFormat = GetViewCastedFormat(displayFormat, cfg.typeCast);
// if the format didn't change we can't re-interpret this format anyway
if(displayFormat != texDetails.internalFormat)
{
GLMarkerRegion castRegion("Casting texture for view");
drv.glGenTextures(1, &castTexture);
drv.glActiveTexture(eGL_TEXTURE0);
drv.glBindTexture(target, castTexture);
// can't use texture views because the underlying image isn't immutable because we don't
// want to rely on texture storage. We also can't rely on texture views, meaning we need a
// fallback path ANYWAY, so might as well always use it. Yayyy opengl...
drv.CreateTextureImage(castTexture, displayFormat, eGL_NONE, eGL_NONE, target,
texDetails.dimension, texDetails.width, texDetails.height,
texDetails.depth, texDetails.samples, (int)numMips);
bool isCompressed = IsCompressedFormat(displayFormat);
GLint values[4] = {};
float fvalues[4] = {};
// ensure source texture is complete
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, values);
int prevMaxLevel = values[0];
int maxlevel = numMips - 1;
GL.glTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, (GLint *)&maxlevel);
// copy state
if(!texDetails.emulated && (HasExt[ARB_texture_swizzle] || HasExt[EXT_texture_swizzle]))
{
GetTextureSwizzle(texname, target, (GLenum *)values);
SetTextureSwizzle(castTexture, target, (GLenum *)values);
}
if((target == eGL_TEXTURE_CUBE_MAP || target == eGL_TEXTURE_CUBE_MAP_ARRAY) &&
HasExt[ARB_seamless_cubemap_per_texture])
{
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_CUBE_MAP_SEAMLESS, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_CUBE_MAP_SEAMLESS, values);
}
if(target != eGL_TEXTURE_2D_MULTISAMPLE && target != eGL_TEXTURE_2D_MULTISAMPLE_ARRAY)
{
if(HasExt[EXT_texture_sRGB_decode])
{
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_SRGB_DECODE_EXT, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_SRGB_DECODE_EXT, values);
}
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_COMPARE_FUNC, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_COMPARE_FUNC, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_COMPARE_MODE, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_COMPARE_MODE, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_MIN_FILTER, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_MIN_FILTER, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_MAG_FILTER, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_MAG_FILTER, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_WRAP_R, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_WRAP_R, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_WRAP_S, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_WRAP_S, values);
GL.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_WRAP_T, values);
GL.glTextureParameterivEXT(castTexture, target, eGL_TEXTURE_WRAP_T, values);
if(HasExt[ARB_texture_border_clamp])
{
GL.glGetTextureParameterfvEXT(texname, target, eGL_TEXTURE_BORDER_COLOR, fvalues);
GL.glTextureParameterfvEXT(castTexture, target, eGL_TEXTURE_BORDER_COLOR, fvalues);
}
if(!IsGLES)
{
GL.glGetTextureParameterfvEXT(texname, target, eGL_TEXTURE_LOD_BIAS, fvalues);
GL.glTextureParameterfvEXT(castTexture, target, eGL_TEXTURE_LOD_BIAS, fvalues);
}
if(target != eGL_TEXTURE_RECTANGLE)
{
GL.glGetTextureParameterfvEXT(texname, target, eGL_TEXTURE_MIN_LOD, fvalues);
GL.glTextureParameterfvEXT(castTexture, target, eGL_TEXTURE_MIN_LOD, fvalues);
GL.glGetTextureParameterfvEXT(texname, target, eGL_TEXTURE_MAX_LOD, fvalues);
GL.glTextureParameterfvEXT(castTexture, target, eGL_TEXTURE_MAX_LOD, fvalues);
}
}
// copy mip data
for(uint32_t i = 0; i < numMips; i++)
{
int w = RDCMAX(texDetails.width >> i, 1);
int h = RDCMAX(texDetails.height >> i, 1);
int d = RDCMAX(texDetails.depth >> i, 1);
if(target == eGL_TEXTURE_CUBE_MAP)
d *= 6;
else if(target == eGL_TEXTURE_CUBE_MAP_ARRAY || target == eGL_TEXTURE_2D_ARRAY)
d = texDetails.depth;
// glCopyImageSubData treats 1D arrays sanely - with depth as array size - but at odds
// with the rest of the API.
if(target == eGL_TEXTURE_1D_ARRAY)
{
h = 1;
d = texDetails.height;
}
if((isCompressed && VendorCheck[VendorCheck_AMD_copy_compressed_tinymips] && (w < 4 || h < 4)) ||
(isCompressed && VendorCheck[VendorCheck_AMD_copy_compressed_cubemaps] &&
texDetails.curType == eGL_TEXTURE_CUBE_MAP) ||
(isCompressed && IsGLES))
{
GLenum targets[] = {
eGL_TEXTURE_CUBE_MAP_POSITIVE_X, eGL_TEXTURE_CUBE_MAP_NEGATIVE_X,
eGL_TEXTURE_CUBE_MAP_POSITIVE_Y, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
eGL_TEXTURE_CUBE_MAP_POSITIVE_Z, eGL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
};
int count = ARRAY_COUNT(targets);
if(target != eGL_TEXTURE_CUBE_MAP)
{
targets[0] = target;
count = 1;
}
for(int trg = 0; trg < count; trg++)
{
size_t size = GetCompressedByteSize(w, h, d, displayFormat);
if(target == eGL_TEXTURE_CUBE_MAP)
size /= 6;
byte *buf = new byte[size];
if(IsGLES)
{
texDetails.GetCompressedImageDataGLES(i, targets[trg], size, buf);
}
else
{
// read to CPU
GL.glGetCompressedTextureImageEXT(texname, targets[trg], i, buf);
}
// write to GPU
if(texDetails.dimension == 1)
GL.glCompressedTextureSubImage1DEXT(castTexture, targets[trg], i, 0, w, displayFormat,
(GLsizei)size, buf);
else if(texDetails.dimension == 2)
GL.glCompressedTextureSubImage2DEXT(castTexture, targets[trg], i, 0, 0, w, h,
displayFormat, (GLsizei)size, buf);
else if(texDetails.dimension == 3)
GL.glCompressedTextureSubImage3DEXT(castTexture, targets[trg], i, 0, 0, 0, w, h, d,
displayFormat, (GLsizei)size, buf);
delete[] buf;
}
}
else
{
GL.glCopyImageSubData(texname, target, i, 0, 0, 0, castTexture, target, i, 0, 0, 0, w, h,
d);
}
}
GL.glTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, (GLint *)&prevMaxLevel);
texname = castTexture;
}
}
RDCGLenum dsTexMode = eGL_NONE;
if(IsDepthStencilFormat(displayFormat))
{
// stencil-only, make sure we display it as such
if(displayFormat == eGL_STENCIL_INDEX8)
{
cfg.red = false;
cfg.green = true;
cfg.blue = false;
cfg.alpha = false;
}
// depth-only, make sure we display it as such
if(GetBaseFormat(displayFormat) == eGL_DEPTH_COMPONENT)
{
cfg.red = true;
cfg.green = false;
cfg.blue = false;
cfg.alpha = false;
}
if(!cfg.red && cfg.green)
{
dsTexMode = eGL_STENCIL_INDEX;
// Stencil texture sampling is not normalized in OpenGL
intIdx = 1;
float rangeScale = 1.0f;
switch(displayFormat)
{
case eGL_STENCIL_INDEX1: rangeScale = 1.0f; break;
case eGL_STENCIL_INDEX4: rangeScale = 16.0f; break;
default:
RDCWARN("Unexpected raw format for stencil visualization");
DELIBERATE_FALLTHROUGH();
case eGL_DEPTH24_STENCIL8:
case eGL_DEPTH32F_STENCIL8:
case eGL_DEPTH_STENCIL:
case eGL_STENCIL_INDEX8: rangeScale = 255.0f; break;
case eGL_STENCIL_INDEX16: rangeScale = 65535.0f; break;
}
cfg.rangeMin *= rangeScale;
cfg.rangeMax *= rangeScale;
}
else
dsTexMode = eGL_DEPTH_COMPONENT;
}
else
{
if(IsUIntFormat(displayFormat))
intIdx = 1;
if(IsSIntFormat(displayFormat))
intIdx = 2;
}
drv.glBindProgramPipeline(0);
if(flags & eTexDisplay_RemapFloat)
drv.glUseProgram(DebugData.texRemapProg[0]);
else if(flags & eTexDisplay_RemapUInt)
drv.glUseProgram(DebugData.texRemapProg[1]);
else if(flags & eTexDisplay_RemapSInt)
drv.glUseProgram(DebugData.texRemapProg[2]);
else
drv.glUseProgram(DebugData.texDisplayProg[intIdx]);
GLuint customProgram = 0;
if(cfg.customShaderId != ResourceId() &&
drv.GetResourceManager()->HasCurrentResource(cfg.customShaderId))
{
GLuint customShader = drv.GetResourceManager()->GetCurrentResource(cfg.customShaderId).name;
customProgram = drv.glCreateProgram();
drv.glAttachShader(customProgram, DebugData.texDisplayVertexShader);
drv.glAttachShader(customProgram, customShader);
drv.glLinkProgram(customProgram);
drv.glDetachShader(customProgram, DebugData.texDisplayVertexShader);
drv.glDetachShader(customProgram, customShader);
char buffer[1024] = {};
GLint status = 0;
drv.glGetProgramiv(customProgram, eGL_LINK_STATUS, &status);
if(status == 0)
{
drv.glGetProgramInfoLog(customProgram, 1024, NULL, buffer);
RDCERR("Error linking custom shader program: %s", buffer);
drv.glDeleteProgram(customProgram);
customProgram = 0;
}
if(customProgram)
{
drv.glUseProgram(customProgram);
GLint loc = -1;
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_TexDim");
if(loc >= 0)
drv.glProgramUniform4ui(customProgram, loc, texDetails.width, texDetails.height,
texDetails.depth, (uint32_t)numMips);
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_SelectedMip");
if(loc >= 0)
drv.glProgramUniform1ui(customProgram, loc, cfg.subresource.mip);
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_SelectedSliceFace");
if(loc >= 0)
drv.glProgramUniform1ui(customProgram, loc, cfg.subresource.slice);
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_SelectedSample");
if(loc >= 0)
{
if(cfg.subresource.sample == ~0U)
drv.glProgramUniform1i(customProgram, loc, -texDetails.samples);
else
drv.glProgramUniform1i(customProgram, loc, (int)RDCCLAMP(cfg.subresource.sample, 0U,
(uint32_t)texDetails.samples - 1));
}
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_TextureType");
if(loc >= 0)
drv.glProgramUniform1ui(customProgram, loc, resType);
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_SelectedRangeMin");
if(loc >= 0)
drv.glProgramUniform1f(customProgram, loc, cfg.rangeMin);
loc = drv.glGetUniformLocation(customProgram, "RENDERDOC_SelectedRangeMax");
if(loc >= 0)
drv.glProgramUniform1f(customProgram, loc, cfg.rangeMax);
}
}
// bind a dummy texbuffer - some drivers (macOS) have trouble when a buffer isn't bound.
if(resType != RESTYPE_TEXBUFFER && DebugData.dummyTexBuffer)
{
drv.glActiveTexture((RDCGLenum)(eGL_TEXTURE0 + RESTYPE_TEXBUFFER));
drv.glBindTexture(eGL_TEXTURE_BUFFER, DebugData.dummyTexBuffer);
}
drv.glActiveTexture((RDCGLenum)(eGL_TEXTURE0 + resType));
drv.glBindTexture(target, texname);
GLint origDSTexMode = eGL_DEPTH_COMPONENT;
if(dsTexMode != eGL_NONE && HasExt[ARB_stencil_texturing])
{
drv.glGetTextureParameterivEXT(texname, target, eGL_DEPTH_STENCIL_TEXTURE_MODE, &origDSTexMode);
drv.glTextureParameteriEXT(texname, target, eGL_DEPTH_STENCIL_TEXTURE_MODE, dsTexMode);
}
// defined as arrays mostly for Coverity code analysis to stay calm about passing
// them to the *TexParameter* functions
GLint baseLevel[4] = {-1};
GLint maxlevel[4] = {-1};
GLint forcedparam[4] = {};
bool levelsTex = (target != eGL_TEXTURE_BUFFER && target != eGL_TEXTURE_2D_MULTISAMPLE &&
target != eGL_TEXTURE_2D_MULTISAMPLE_ARRAY);
if(levelsTex)
{
drv.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_BASE_LEVEL, baseLevel);
drv.glGetTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, maxlevel);
}
else
{
baseLevel[0] = maxlevel[0] = -1;
}
// ensure texture is mipmap complete and we can view all mips (if the range has been reduced) by
// forcing TEXTURE_MAX_LEVEL to cover all valid mips.
if(levelsTex && cfg.resourceId != DebugData.CustomShaderTexID)
{
forcedparam[0] = 0;
drv.glTextureParameterivEXT(texname, target, eGL_TEXTURE_BASE_LEVEL, forcedparam);
forcedparam[0] = GLint(numMips - 1);
drv.glTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, forcedparam);
}
else
{
maxlevel[0] = -1;
}
bool intTexture = intIdx > 0 || (flags & eTexDisplay_RemapUInt) || (flags & eTexDisplay_RemapSInt);
GLint tex_x = texDetails.width, tex_y = texDetails.height, tex_z = texDetails.depth;
drv.glBindBufferBase(eGL_UNIFORM_BUFFER, 0, DebugData.UBOs[0]);
TexDisplayUBOData *ubo =
(TexDisplayUBOData *)drv.glMapBufferRange(eGL_UNIFORM_BUFFER, 0, sizeof(TexDisplayUBOData),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
float x = cfg.xOffset;
float y = cfg.yOffset;
ubo->Position.x = x;
ubo->Position.y = y;
ubo->Scale = cfg.scale;
if(cfg.scale <= 0.0f)
{
float xscale = DebugData.outWidth / float(tex_x);
float yscale = DebugData.outHeight / float(tex_y);
ubo->Scale = cfg.scale = RDCMIN(xscale, yscale);
if(yscale > xscale)
{
ubo->Position.x = 0;
ubo->Position.y = (DebugData.outHeight - (tex_y * ubo->Scale)) * 0.5f;
}
else
{
ubo->Position.y = 0;
ubo->Position.x = (DebugData.outWidth - (tex_x * ubo->Scale)) * 0.5f;
}
}
ubo->HDRMul = cfg.hdrMultiplier;
ubo->FlipY = cfg.flipY ? 1 : 0;
if(cfg.rangeMax <= cfg.rangeMin)
cfg.rangeMax += 0.00001f;
if(dsTexMode == eGL_NONE)
{
ubo->Channels.x = cfg.red ? 1.0f : 0.0f;
ubo->Channels.y = cfg.green ? 1.0f : 0.0f;
ubo->Channels.z = cfg.blue ? 1.0f : 0.0f;
ubo->Channels.w = cfg.alpha ? 1.0f : 0.0f;
}
else
{
// Both depth and stencil texture mode use the red channel
ubo->Channels.x = 1.0f;
ubo->Channels.y = 0.0f;
ubo->Channels.z = 0.0f;
ubo->Channels.w = 0.0f;
}
ubo->RangeMinimum = cfg.rangeMin;
ubo->InverseRangeSize = 1.0f / (cfg.rangeMax - cfg.rangeMin);
TextureSamplerMode mode = TextureSamplerMode::Point;
if(cfg.subresource.mip == 0 && cfg.scale < 1.0f && dsTexMode == eGL_NONE &&
resType != RESTYPE_TEXBUFFER && resType != RESTYPE_TEXRECT && !intTexture)
{
mode = TextureSamplerMode::Linear;
}
else
{
if(resType == RESTYPE_TEXRECT || resType == RESTYPE_TEX2DMS ||
resType == RESTYPE_TEX2DMSARRAY || resType == RESTYPE_TEXBUFFER)
mode = TextureSamplerMode::PointNoMip;
else
mode = TextureSamplerMode::Point;
}
ubo->MipLevel = (int)cfg.subresource.mip;
if(texDetails.curType != eGL_TEXTURE_3D)
{
uint32_t numSlices = RDCMAX((uint32_t)texDetails.depth, 1U);
if(texDetails.curType == eGL_TEXTURE_CUBE_MAP)
numSlices *= 6;
if(texDetails.curType == eGL_TEXTURE_1D_ARRAY)
numSlices = RDCMAX((uint32_t)texDetails.height, 1U);
uint32_t sliceFace = RDCCLAMP(cfg.subresource.slice, 0U, numSlices - 1);
ubo->Slice = (float)sliceFace + 0.001f;
}
else
{
float slice =
(float)RDCCLAMP(cfg.subresource.slice, 0U, RDCMAX((uint32_t)texDetails.depth, 1U) - 1);
// when sampling linearly, we need to add half a pixel to ensure we only sample the desired
// slice
if(mode == TextureSamplerMode::Linear)
slice += 0.5f;
else
slice += 0.001f;
ubo->Slice = slice;
}
ubo->OutputDisplayFormat = resType;
if(cfg.overlay == DebugOverlay::NaN)
ubo->OutputDisplayFormat |= TEXDISPLAY_NANS;
if(cfg.overlay == DebugOverlay::Clipping)
ubo->OutputDisplayFormat |= TEXDISPLAY_CLIPPING;
if(!IsSRGBFormat(displayFormat) && cfg.linearDisplayAsGamma)
ubo->OutputDisplayFormat |= TEXDISPLAY_GAMMA_CURVE;
ubo->RawOutput = cfg.rawOutput ? 1 : 0;
ubo->TextureResolutionPS.x = float(RDCMAX(1, tex_x >> cfg.subresource.mip));
ubo->TextureResolutionPS.y = float(RDCMAX(1, tex_y >> cfg.subresource.mip));
ubo->TextureResolutionPS.z = float(RDCMAX(1, tex_z >> cfg.subresource.mip));
if(mipShift)
ubo->MipShift = float(1 << cfg.subresource.mip);
else
ubo->MipShift = 1.0f;
ubo->OutputRes.x = DebugData.outWidth;
ubo->OutputRes.y = DebugData.outHeight;
ubo->SampleIdx = (int)RDCCLAMP(cfg.subresource.sample, 0U, (uint32_t)texDetails.samples - 1);
// hacky resolve
if(cfg.subresource.sample == ~0U)
ubo->SampleIdx = -texDetails.samples;
ubo->DecodeYUV = 0;
ubo->YUVDownsampleRate = {};
ubo->YUVAChannels = {};
drv.glUnmapBuffer(eGL_UNIFORM_BUFFER);
TextureSamplerState prevSampState = SetSamplerParams(target, texname, mode);
HeatmapData heatmapData = {};
{
if(cfg.overlay == DebugOverlay::QuadOverdrawDraw || cfg.overlay == DebugOverlay::QuadOverdrawPass)
{
heatmapData.HeatmapMode = HEATMAP_LINEAR;
}
else if(cfg.overlay == DebugOverlay::TriangleSizeDraw ||
cfg.overlay == DebugOverlay::TriangleSizePass)
{
heatmapData.HeatmapMode = HEATMAP_TRISIZE;
}
if(heatmapData.HeatmapMode)
{
memcpy(heatmapData.ColorRamp, colorRamp, sizeof(colorRamp));
RDCCOMPILE_ASSERT(sizeof(heatmapData.ColorRamp) == sizeof(colorRamp),
"C++ color ramp array is not the same size as the shader array");
}
}
drv.glBindBufferBase(eGL_UNIFORM_BUFFER, 1, DebugData.UBOs[1]);
{
HeatmapData *ptr = (HeatmapData *)drv.glMapBufferRange(
eGL_UNIFORM_BUFFER, 0, sizeof(HeatmapData), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
memcpy(ptr, &heatmapData, sizeof(heatmapData));
drv.glUnmapBuffer(eGL_UNIFORM_BUFFER);
}
if(cfg.rawOutput || !blendAlpha || cfg.customShaderId != ResourceId())
{
drv.glDisable(eGL_BLEND);
}
else
{
drv.glEnable(eGL_BLEND);
drv.glBlendFunc(eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA);
}
drv.glDisable(eGL_DEPTH_TEST);
if(HasExt[EXT_framebuffer_sRGB])
drv.glEnable(eGL_FRAMEBUFFER_SRGB);
drv.glBindVertexArray(DebugData.emptyVAO);
drv.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4);
if(baseLevel[0] >= 0)
drv.glTextureParameterivEXT(texname, target, eGL_TEXTURE_BASE_LEVEL, baseLevel);
if(maxlevel[0] >= 0)
drv.glTextureParameterivEXT(texname, target, eGL_TEXTURE_MAX_LEVEL, maxlevel);
RestoreSamplerParams(target, texname, prevSampState);
if(customProgram)
{
drv.glUseProgram(0);
drv.glDeleteProgram(customProgram);
}
if(dsTexMode != eGL_NONE && HasExt[ARB_stencil_texturing])
drv.glTextureParameteriEXT(texname, target, eGL_DEPTH_STENCIL_TEXTURE_MODE, origDSTexMode);
if(castTexture)
drv.glDeleteTextures(1, &castTexture);
return true;
}