Skip to content

Commit fc68cd9

Browse files
GLES: Add debug readback of stencil data.
This allows the existing gpu.buffer.renderStencil to snapshot the state.
1 parent c03d327 commit fc68cd9

File tree

6 files changed

+128
-1
lines changed

6 files changed

+128
-1
lines changed

Common/GPU/OpenGL/GLFeatures.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -544,8 +544,8 @@ void CheckGLExtensions() {
544544
}
545545
if (gl_extensions.VersionGEThan(4, 3)) {
546546
gl_extensions.ARB_copy_image = true;
547+
gl_extensions.ARB_stencil_texturing = true;
547548
// ARB_explicit_uniform_location = true;
548-
// ARB_stencil_texturing = true;
549549
// ARB_texture_view = true;
550550
// ARB_vertex_attrib_binding = true;
551551
}

Common/GPU/OpenGL/GLFeatures.h

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct GLExtensions {
7171
bool ARB_depth_clamp;
7272
bool ARB_uniform_buffer_object;
7373
bool ARB_texture_non_power_of_two;
74+
bool ARB_stencil_texturing;
7475

7576
// EXT
7677
bool EXT_swap_control_tear;

GPU/Common/FramebufferManagerCommon.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -2578,6 +2578,10 @@ bool FramebufferManagerCommon::GetStencilbuffer(u32 fb_address, int fb_stride, G
25782578
// No need to free on failure, the caller/destructor will do that. Usually this is a reused buffer, anyway.
25792579
buffer.Allocate(w, h, GPU_DBG_FORMAT_8BIT, flipY);
25802580
bool retval = draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, w,h, Draw::DataFormat::S8, buffer.GetData(), w, "GetStencilbuffer");
2581+
if (!retval) {
2582+
// Try ReadbackStencilbufferSync, in case GLES.
2583+
retval = ReadbackStencilbufferSync(vfb->fbo, 0, 0, w, h, buffer.GetData(), w);
2584+
}
25812585
// That may have unbound the framebuffer, rebind to avoid crashes when debugging.
25822586
RebindFramebuffer("RebindFramebuffer - GetStencilbuffer");
25832587
return retval;
@@ -2652,6 +2656,10 @@ bool FramebufferManagerCommon::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, i
26522656
return draw_->CopyFramebufferToMemorySync(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, destFormat, pixels, pixelsStride, "ReadbackDepthbufferSync");
26532657
}
26542658

2659+
bool FramebufferManagerCommon::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) {
2660+
return draw_->CopyFramebufferToMemorySync(fbo, Draw::FB_DEPTH_BIT, x, y, w, h, Draw::DataFormat::S8, pixels, pixelsStride, "ReadbackStencilbufferSync");
2661+
}
2662+
26552663
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) {
26562664
// Clamp to bufferWidth. Sometimes block transfers can cause this to hit.
26572665
if (x + w >= vfb->bufferWidth) {
@@ -2809,6 +2817,8 @@ void FramebufferManagerCommon::DeviceLost() {
28092817
}
28102818
DoRelease(stencilWriteSampler_);
28112819
DoRelease(stencilWritePipeline_);
2820+
DoRelease(stencilReadbackSampler_);
2821+
DoRelease(stencilReadbackPipeline_);
28122822
DoRelease(depthReadbackSampler_);
28132823
DoRelease(depthReadbackPipeline_);
28142824
DoRelease(draw2DPipelineColor_);

GPU/Common/FramebufferManagerCommon.h

+4
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ class FramebufferManagerCommon {
444444
virtual void ReadbackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel);
445445
// Used for when a shader is required, such as GLES.
446446
virtual bool ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride);
447+
virtual bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride);
447448
void SetViewport2D(int x, int y, int w, int h);
448449
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height);
449450
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags);
@@ -571,6 +572,9 @@ class FramebufferManagerCommon {
571572
Draw::Pipeline *stencilWritePipeline_ = nullptr;
572573
Draw::SamplerState *stencilWriteSampler_ = nullptr;
573574

575+
// Used on GLES where we can't directly readback depth or stencil, but here for simplicity.
576+
Draw::Pipeline *stencilReadbackPipeline_ = nullptr;
577+
Draw::SamplerState *stencilReadbackSampler_ = nullptr;
574578
Draw::Pipeline *depthReadbackPipeline_ = nullptr;
575579
Draw::SamplerState *depthReadbackSampler_ = nullptr;
576580

GPU/GLES/DepthBufferGLES.cpp

+111
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,45 @@ const UniformBufferDesc depthUBDesc{ sizeof(DepthUB), {
8585
{ "u_depthTo8", -1, -1, UniformType::FLOAT4, 32 },
8686
} };
8787

88+
static const char *stencil_dl_fs = R"(
89+
#ifdef GL_ES
90+
#ifdef GL_FRAGMENT_PRECISION_HIGH
91+
precision highp float;
92+
#else
93+
precision mediump float;
94+
#endif
95+
#endif
96+
#if __VERSION__ >= 130
97+
#define varying in
98+
#define texture2D texture
99+
#define gl_FragColor fragColor0
100+
out vec4 fragColor0;
101+
#endif
102+
varying vec2 v_texcoord;
103+
lowp uniform usampler2D tex;
104+
void main() {
105+
uint stencil = texture2D(tex, v_texcoord).r;
106+
float scaled = float(stencil) / 255.0;
107+
gl_FragColor = vec4(scaled, scaled, scaled, scaled);
108+
}
109+
)";
110+
111+
static const char *stencil_vs = R"(
112+
#ifdef GL_ES
113+
precision highp float;
114+
#endif
115+
#if __VERSION__ >= 130
116+
#define attribute in
117+
#define varying out
118+
#endif
119+
attribute vec2 a_position;
120+
varying vec2 v_texcoord;
121+
void main() {
122+
v_texcoord = a_position * 2.0;
123+
gl_Position = vec4(v_texcoord * 2.0 - vec2(1.0, 1.0), 0.0, 1.0);
124+
}
125+
)";
126+
88127
static bool SupportsDepthTexturing() {
89128
if (gl_extensions.IsGLES) {
90129
return gl_extensions.OES_packed_depth_stencil && (gl_extensions.OES_depth_texture || gl_extensions.GLES3);
@@ -248,3 +287,75 @@ bool FramebufferManagerGLES::ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int
248287
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
249288
return true;
250289
}
290+
291+
// Well, this is not depth, but it's depth/stencil related.
292+
bool FramebufferManagerGLES::ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) {
293+
using namespace Draw;
294+
295+
if (!fbo) {
296+
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "ReadbackStencilbufferSync: bad fbo");
297+
return false;
298+
}
299+
300+
const bool useColorPath = gl_extensions.IsGLES;
301+
if (!useColorPath) {
302+
return draw_->CopyFramebufferToMemorySync(fbo, FB_STENCIL_BIT, x, y, w, h, DataFormat::S8, pixels, pixelsStride, "ReadbackStencilbufferSync");
303+
}
304+
305+
// Unsupported below GLES 3.1 or without ARB_stencil_texturing.
306+
// OES_texture_stencil8 is related, but used to specify texture data.
307+
if ((gl_extensions.IsGLES && !gl_extensions.VersionGEThan(3, 1)) && !gl_extensions.ARB_stencil_texturing)
308+
return false;
309+
310+
// Pixel size always 4 here because we always request RGBA back.
311+
const u32 bufSize = w * h * 4;
312+
if (!convBuf_ || convBufSize_ < bufSize) {
313+
delete[] convBuf_;
314+
convBuf_ = new u8[bufSize];
315+
convBufSize_ = bufSize;
316+
}
317+
318+
if (!stencilReadbackPipeline_) {
319+
stencilReadbackPipeline_ = CreateReadbackPipeline(draw_, "stencil_dl", &depthUBDesc, stencil_dl_fs, "stencil_dl_fs", stencil_vs, "stencil_vs");
320+
stencilReadbackSampler_ = draw_->CreateSamplerState({});
321+
}
322+
323+
shaderManager_->DirtyLastShader();
324+
auto *blitFBO = GetTempFBO(TempFBO::COPY, fbo->Width(), fbo->Height());
325+
draw_->BindFramebufferAsRenderTarget(blitFBO, { RPAction::DONT_CARE, RPAction::DONT_CARE, RPAction::DONT_CARE }, "ReadbackStencilbufferSync");
326+
Draw::Viewport viewport = { 0.0f, 0.0f, (float)fbo->Width(), (float)fbo->Height(), 0.0f, 1.0f };
327+
draw_->SetViewports(1, &viewport);
328+
329+
draw_->BindFramebufferAsTexture(fbo, TEX_SLOT_PSP_TEXTURE, FB_STENCIL_BIT, 0);
330+
draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, &stencilReadbackSampler_);
331+
332+
// We must bind the program after starting the render pass.
333+
draw_->SetScissorRect(0, 0, w, h);
334+
draw_->BindPipeline(stencilReadbackPipeline_);
335+
336+
// Fullscreen triangle coordinates.
337+
static const float positions[6] = {
338+
0.0, 0.0,
339+
1.0, 0.0,
340+
0.0, 1.0,
341+
};
342+
draw_->DrawUP(positions, 3);
343+
344+
draw_->CopyFramebufferToMemorySync(blitFBO, FB_COLOR_BIT, x, y, w, h, DataFormat::R8G8B8A8_UNORM, convBuf_, w, "ReadbackStencilbufferSync");
345+
346+
textureCache_->ForgetLastTexture();
347+
348+
// TODO: Use 1/4 width to write all values directly and skip CPU conversion?
349+
uint8_t *dest = pixels;
350+
const u32_le *packed32 = (u32_le *)convBuf_;
351+
for (int yp = 0; yp < h; ++yp) {
352+
for (int xp = 0; xp < w; ++xp) {
353+
dest[xp] = packed32[xp] & 0xFF;
354+
}
355+
dest += pixelsStride;
356+
packed32 += w;
357+
}
358+
359+
gstate_c.Dirty(DIRTY_ALL_RENDER_STATE);
360+
return true;
361+
}

GPU/GLES/FramebufferManagerGLES.h

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class FramebufferManagerGLES : public FramebufferManagerCommon {
3939
protected:
4040
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
4141
bool ReadbackDepthbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint16_t *pixels, int pixelsStride) override;
42+
bool ReadbackStencilbufferSync(Draw::Framebuffer *fbo, int x, int y, int w, int h, uint8_t *pixels, int pixelsStride) override;
4243

4344
private:
4445
u8 *convBuf_ = nullptr;

0 commit comments

Comments
 (0)