@@ -85,6 +85,45 @@ const UniformBufferDesc depthUBDesc{ sizeof(DepthUB), {
85
85
{ " u_depthTo8" , -1 , -1 , UniformType::FLOAT4, 32 },
86
86
} };
87
87
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
+
88
127
static bool SupportsDepthTexturing () {
89
128
if (gl_extensions.IsGLES ) {
90
129
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
248
287
gstate_c.Dirty (DIRTY_ALL_RENDER_STATE);
249
288
return true ;
250
289
}
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
+ }
0 commit comments