1 // Copyright (c) 2012- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include <algorithm>
19
20 #include "Common/GPU/OpenGL/GLFeatures.h"
21 #include "Core/ConfigValues.h"
22 #include "Core/Reporting.h"
23 #include "GPU/Common/GPUStateUtils.h"
24 #include "GPU/GLES/DrawEngineGLES.h"
25 #include "GPU/GLES/FramebufferManagerGLES.h"
26 #include "GPU/GLES/ShaderManagerGLES.h"
27 #include "GPU/GLES/TextureCacheGLES.h"
28
29 static const char *depth_dl_fs = R"(
30 #ifdef GL_ES
31 #ifdef GL_FRAGMENT_PRECISION_HIGH
32 precision highp float;
33 #else
34 precision mediump float;
35 #endif
36 #endif
37 #if __VERSION__ >= 130
38 #define varying in
39 #define texture2D texture
40 #define gl_FragColor fragColor0
41 out vec4 fragColor0;
42 #endif
43 varying vec2 v_texcoord0;
44 uniform vec2 u_depthFactor;
45 uniform vec4 u_depthShift;
46 uniform vec4 u_depthTo8;
47 uniform sampler2D tex;
48 void main() {
49 float depth = texture2D(tex, v_texcoord0).r;
50 // At this point, clamped maps [0, 1] to [0, 65535].
51 float clamped = clamp((depth + u_depthFactor.x) * u_depthFactor.y, 0.0, 1.0);
52
53 vec4 enc = u_depthShift * clamped;
54 enc = floor(mod(enc, 256.0)) * u_depthTo8;
55 enc = enc * u_depthTo8;
56 // Let's ignore the bits outside 16 bit precision.
57 gl_FragColor = enc.yzww;
58 }
59 )";
60
61 static const char *depth_vs = R"(
62 #ifdef GL_ES
63 precision highp float;
64 #endif
65 #if __VERSION__ >= 130
66 #define attribute in
67 #define varying out
68 #endif
69 attribute vec4 a_position;
70 attribute vec2 a_texcoord0;
71 varying vec2 v_texcoord0;
72 void main() {
73 v_texcoord0 = a_texcoord0;
74 gl_Position = a_position;
75 }
76 )";
77
PackDepthbuffer(VirtualFramebuffer * vfb,int x,int y,int w,int h)78 void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
79 if (!vfb->fbo) {
80 ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
81 return;
82 }
83
84 // Pixel size always 4 here because we always request float
85 const u32 bufSize = vfb->z_stride * (h - y) * 4;
86 const u32 z_address = vfb->z_address;
87 const int packWidth = std::min(vfb->z_stride, std::min(x + w, (int)vfb->width));
88
89 if (!convBuf_ || convBufSize_ < bufSize) {
90 delete[] convBuf_;
91 convBuf_ = new u8[bufSize];
92 convBufSize_ = bufSize;
93 }
94
95 DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address);
96
97 const bool useColorPath = gl_extensions.IsGLES;
98 bool format16Bit = false;
99
100 if (useColorPath) {
101 if (!depthDownloadProgram_) {
102 std::string errorString;
103 static std::string vs_code, fs_code;
104 vs_code = ApplyGLSLPrelude(depth_vs, GL_VERTEX_SHADER);
105 fs_code = ApplyGLSLPrelude(depth_dl_fs, GL_FRAGMENT_SHADER);
106 std::vector<GLRShader *> shaders;
107 shaders.push_back(render_->CreateShader(GL_VERTEX_SHADER, vs_code, "depth_dl"));
108 shaders.push_back(render_->CreateShader(GL_FRAGMENT_SHADER, fs_code, "depth_dl"));
109 std::vector<GLRProgram::Semantic> semantics;
110 semantics.push_back({ 0, "a_position" });
111 semantics.push_back({ 1, "a_texcoord0" });
112 std::vector<GLRProgram::UniformLocQuery> queries;
113 queries.push_back({ &u_depthDownloadTex, "tex" });
114 queries.push_back({ &u_depthDownloadFactor, "u_depthFactor" });
115 queries.push_back({ &u_depthDownloadShift, "u_depthShift" });
116 queries.push_back({ &u_depthDownloadTo8, "u_depthTo8" });
117 std::vector<GLRProgram::Initializer> inits;
118 inits.push_back({ &u_depthDownloadTex, 0, TEX_SLOT_PSP_TEXTURE });
119 depthDownloadProgram_ = render_->CreateProgram(shaders, semantics, queries, inits, false);
120 for (auto iter : shaders) {
121 render_->DeleteShader(iter);
122 }
123 if (!depthDownloadProgram_) {
124 ERROR_LOG_REPORT(G3D, "Failed to compile depthDownloadProgram! This shouldn't happen.\n%s", errorString.c_str());
125 }
126 }
127
128 shaderManagerGL_->DirtyLastShader();
129 auto *blitFBO = GetTempFBO(TempFBO::COPY, vfb->renderWidth, vfb->renderHeight);
130 draw_->BindFramebufferAsRenderTarget(blitFBO, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PackDepthbuffer");
131 render_->SetViewport({ 0, 0, (float)vfb->renderWidth, (float)vfb->renderHeight, 0.0f, 1.0f });
132
133 // We must bind the program after starting the render pass, and set the color mask after clearing.
134 render_->SetScissor({ 0, 0, vfb->renderWidth, vfb->renderHeight });
135 render_->SetDepth(false, false, GL_ALWAYS);
136 render_->SetRaster(false, GL_CCW, GL_FRONT, GL_FALSE, GL_FALSE);
137 render_->BindProgram(depthDownloadProgram_);
138
139 if (!gstate_c.Supports(GPU_SUPPORTS_ACCURATE_DEPTH)) {
140 float factors[] = { 0.0f, 1.0f };
141 render_->SetUniformF(&u_depthDownloadFactor, 2, factors);
142 } else {
143 const float factor = DepthSliceFactor();
144 float factors[] = { -0.5f * (factor - 1.0f) * (1.0f / factor), factor };
145 render_->SetUniformF(&u_depthDownloadFactor, 2, factors);
146 }
147 float shifts[] = { 16777215.0f, 16777215.0f / 256.0f, 16777215.0f / 65536.0f, 16777215.0f / 16777216.0f };
148 render_->SetUniformF(&u_depthDownloadShift, 4, shifts);
149 float to8[] = { 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f };
150 render_->SetUniformF(&u_depthDownloadTo8, 4, to8);
151
152 draw_->BindFramebufferAsTexture(vfb->fbo, TEX_SLOT_PSP_TEXTURE, Draw::FB_DEPTH_BIT, 0);
153 float u1 = 1.0f;
154 float v1 = 1.0f;
155 DrawActiveTexture(x, y, w, h, vfb->renderWidth, vfb->renderHeight, 0.0f, 0.0f, u1, v1, ROTATION_LOCKED_HORIZONTAL, DRAWTEX_NEAREST);
156
157 draw_->CopyFramebufferToMemorySync(blitFBO, Draw::FB_COLOR_BIT, 0, y, packWidth, h, Draw::DataFormat::R8G8B8A8_UNORM, convBuf_, vfb->z_stride, "PackDepthbuffer");
158
159 textureCacheGL_->ForgetLastTexture();
160 // TODO: Use 4444 so we can copy lines directly?
161 format16Bit = true;
162 } else {
163 draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, y, packWidth, h, Draw::DataFormat::D32F, convBuf_, vfb->z_stride, "PackDepthbuffer");
164 format16Bit = false;
165 }
166
167 int dstByteOffset = y * vfb->z_stride * sizeof(u16);
168 u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset);
169 u32_le *packed32 = (u32_le *)convBuf_;
170 GLfloat *packedf = (GLfloat *)convBuf_;
171
172 int totalPixels = h == 1 ? packWidth : vfb->z_stride * h;
173 if (format16Bit) {
174 // TODO: We have to apply GetDepthScaleFactors here too, right?
175 for (int yp = 0; yp < h; ++yp) {
176 int row_offset = vfb->z_stride * yp;
177 for (int xp = 0; xp < packWidth; ++xp) {
178 const int i = row_offset + xp;
179 depth[i] = packed32[i] & 0xFFFF;
180 }
181 }
182 } else {
183 // TODO: Apply this in the shader.
184 DepthScaleFactors depthScale = GetDepthScaleFactors();
185 for (int yp = 0; yp < h; ++yp) {
186 int row_offset = vfb->z_stride * yp;
187 for (int xp = 0; xp < packWidth; ++xp) {
188 const int i = row_offset + xp;
189 float scaled = depthScale.Apply(packedf[i]);
190 if (scaled <= 0.0f) {
191 depth[i] = 0;
192 } else if (scaled >= 65535.0f) {
193 depth[i] = 65535;
194 } else {
195 depth[i] = (int)scaled;
196 }
197 }
198 }
199 }
200
201 gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS);
202 }
203