Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/GPU/Common/Draw2D.cpp
3187 views
1
// Copyright (c) 2014- 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 "Common/GPU/Shader.h"
19
#include "Common/GPU/ShaderWriter.h"
20
#include "Common/GPU/thin3d.h"
21
#include "GPU/Common/Draw2D.h"
22
#include "GPU/Common/DrawEngineCommon.h"
23
#include "GPU/Common/FramebufferManagerCommon.h"
24
#include "GPU/Common/GPUStateUtils.h"
25
26
static const InputDef inputs[2] = {
27
{ "vec2", "a_position", Draw::SEM_POSITION },
28
{ "vec2", "a_texcoord0", Draw::SEM_TEXCOORD0 },
29
};
30
31
static const VaryingDef varyings[1] = {
32
{ "vec2", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" },
33
};
34
35
static const SamplerDef samplers[1] = {
36
{ 0, "tex", SamplerFlags::ARRAY_ON_VULKAN },
37
};
38
39
const UniformDef g_draw2Duniforms[5] = {
40
{ "vec2", "texSize", 0 },
41
{ "float", "scaleFactor", 1},
42
{ "float", "z_scale", 2 },
43
{ "float", "z_scale_inv", 3 },
44
{ "float", "z_offset", 4 },
45
};
46
47
struct Draw2DUB {
48
float texSizeX;
49
float texSizeY;
50
float scaleFactor;
51
float zScale;
52
float zScaleInv;
53
float zOffset;
54
};
55
56
const UniformBufferDesc draw2DUBDesc{ sizeof(Draw2DUB), {
57
{ "texSize", -1, 0, UniformType::FLOAT2, 0 },
58
{ "scaleFactor", -1, 1, UniformType::FLOAT1, 8 },
59
{ "z_scale", -1, 1, UniformType::FLOAT1, 12 },
60
{ "z_scale_inv", -1, 1, UniformType::FLOAT1, 16 },
61
{ "z_offset", -1, 1, UniformType::FLOAT1, 20 },
62
} };
63
64
Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) {
65
writer.DeclareSamplers(samplers);
66
writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);
67
writer.C(" vec4 outColor = ").SampleTexture2D("tex", "v_texcoord.xy").C(";\n");
68
writer.EndFSMain("outColor");
69
70
return Draw2DPipelineInfo{
71
"draw2d_copy_color",
72
RASTER_COLOR,
73
RASTER_COLOR,
74
};
75
}
76
77
Draw2DPipelineInfo GenerateDraw2DCopyColorRect2LinFs(ShaderWriter &writer) {
78
writer.DeclareSamplers(samplers);
79
writer.BeginFSMain(g_draw2Duniforms, varyings);
80
writer.C(" vec2 tSize = texSize / scaleFactor;\n");
81
writer.C(" vec2 pixels = v_texcoord * tSize;\n");
82
writer.C(" float u = mod(pixels.x, tSize.x);\n");
83
writer.C(" float v = floor(pixels.x / tSize.x);\n");
84
writer.C(" vec4 outColor = ").SampleTexture2D("tex", "vec2(u, v) / tSize").C(";\n");
85
writer.EndFSMain("outColor");
86
87
return Draw2DPipelineInfo{
88
"draw2d_copy_color_rect2lin",
89
RASTER_COLOR,
90
RASTER_COLOR,
91
};
92
}
93
94
Draw2DPipelineInfo GenerateDraw2DCopyDepthFs(ShaderWriter &writer) {
95
writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);
96
writer.DeclareSamplers(samplers);
97
writer.BeginFSMain(Slice<UniformDef>::empty(), varyings);
98
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
99
writer.C(" gl_FragDepth = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n");
100
writer.EndFSMain("outColor");
101
102
return Draw2DPipelineInfo{
103
"draw2d_copy_depth",
104
RASTER_DEPTH, // Unused in this case, I think.
105
RASTER_DEPTH,
106
};
107
}
108
109
Draw2DPipelineInfo GenerateDraw2DEncodeDepthFs(ShaderWriter &writer) {
110
writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);
111
writer.HighPrecisionFloat();
112
writer.DeclareSamplers(samplers);
113
writer.BeginFSMain(g_draw2Duniforms, varyings);
114
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
115
writer.C(" float depthValue = ").SampleTexture2D("tex", "v_texcoord.xy").C(".x;\n");
116
writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");
117
writer.EndFSMain("outColor");
118
119
return Draw2DPipelineInfo{
120
"draw2d_copy_r16_to_depth",
121
RASTER_COLOR,
122
RASTER_DEPTH,
123
};
124
}
125
126
Draw2DPipelineInfo GenerateDraw2D565ToDepthFs(ShaderWriter &writer) {
127
writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);
128
writer.HighPrecisionFloat();
129
writer.DeclareSamplers(samplers);
130
writer.BeginFSMain(g_draw2Duniforms, varyings);
131
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
132
// Unlike when just copying a depth buffer, here we're generating new depth values so we'll
133
// have to apply the scaling.
134
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
135
writer.C(" vec3 rgb = ").SampleTexture2D("tex", "v_texcoord.xy").C(".xyz;\n");
136
writer.F(" float depthValue = ((floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0)) / 65535.0; \n");
137
writer.C(" gl_FragDepth = (depthValue * z_scale_inv) + z_offset;\n");
138
writer.EndFSMain("outColor");
139
140
return Draw2DPipelineInfo{
141
"draw2d_565_to_depth",
142
RASTER_COLOR,
143
RASTER_DEPTH,
144
};
145
}
146
147
Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) {
148
writer.SetFlags(ShaderWriterFlags::FS_WRITE_DEPTH);
149
writer.HighPrecisionFloat();
150
writer.DeclareSamplers(samplers);
151
writer.BeginFSMain(g_draw2Duniforms, varyings);
152
writer.C(" vec4 outColor = vec4(0.0, 0.0, 0.0, 0.0);\n");
153
// Unlike when just copying a depth buffer, here we're generating new depth values so we'll
154
// have to apply the scaling.
155
DepthScaleFactors factors = GetDepthScaleFactors(gstate_c.UseFlags());
156
writer.C(" vec2 tsize = texSize;\n");
157
writer.C(" vec2 coord = v_texcoord * tsize;\n");
158
writer.F(" float strip = 4.0 * scaleFactor;\n");
159
writer.C(" float in_strip = mod(coord.y, strip);\n");
160
writer.C(" coord.y = coord.y - in_strip + strip - in_strip;\n");
161
writer.C(" coord /= tsize;\n");
162
writer.C(" highp vec3 rgb = ").SampleTexture2D("tex", "coord").C(".xyz;\n");
163
writer.F(" highp float depthValue = floor(rgb.x * 31.99) + floor(rgb.y * 63.99) * 32.0 + floor(rgb.z * 31.99) * 2048.0; \n");
164
writer.C(" gl_FragDepth = z_offset + ((depthValue / 65535.0) * z_scale_inv);\n");
165
writer.EndFSMain("outColor");
166
167
return Draw2DPipelineInfo{
168
"draw2d_565_to_depth_deswizzle",
169
RASTER_COLOR,
170
RASTER_DEPTH
171
};
172
}
173
174
void GenerateDraw2DVS(ShaderWriter &writer) {
175
writer.BeginVSMain(inputs, Slice<UniformDef>::empty(), varyings);
176
177
writer.C(" v_texcoord = a_texcoord0;\n"); // yes, this should be right. Should be 2.0 in the far corners.
178
writer.C(" gl_Position = vec4(a_position, 0.0, 1.0);\n");
179
180
writer.EndVSMain(varyings);
181
}
182
183
template <typename T>
184
static void DoRelease(T *&obj) {
185
if (obj)
186
obj->Release();
187
obj = nullptr;
188
}
189
190
void Draw2D::DeviceLost() {
191
DoRelease(draw2DVs_);
192
DoRelease(draw2DSamplerLinear_);
193
DoRelease(draw2DSamplerNearest_);
194
draw_ = nullptr;
195
}
196
197
void Draw2D::DeviceRestore(Draw::DrawContext *draw) {
198
draw_ = draw;
199
}
200
201
void Draw2D::Ensure2DResources() {
202
using namespace Draw;
203
204
const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();
205
206
if (!draw2DVs_) {
207
char *vsCode = new char[8192];
208
ShaderWriterFlags flags = ShaderWriterFlags::NONE;
209
if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {
210
// Hm, we're compiling the vertex shader here, probably don't need this...
211
flags = ShaderWriterFlags::FS_AUTO_STEREO;
212
}
213
ShaderWriter writer(vsCode, shaderLanguageDesc, ShaderStage::Vertex);
214
GenerateDraw2DVS(writer);
215
_assert_msg_(strlen(vsCode) < 8192, "Draw2D VS length error: %d", (int)strlen(vsCode));
216
draw2DVs_ = draw_->CreateShaderModule(ShaderStage::Vertex, shaderLanguageDesc.shaderLanguage, (const uint8_t *)vsCode, strlen(vsCode), "draw2d_vs");
217
_assert_(draw2DVs_);
218
delete[] vsCode;
219
}
220
221
if (!draw2DSamplerLinear_) {
222
SamplerStateDesc descLinear{};
223
descLinear.magFilter = TextureFilter::LINEAR;
224
descLinear.minFilter = TextureFilter::LINEAR;
225
descLinear.mipFilter = TextureFilter::LINEAR;
226
descLinear.wrapU = TextureAddressMode::CLAMP_TO_EDGE;
227
descLinear.wrapV = TextureAddressMode::CLAMP_TO_EDGE;
228
descLinear.wrapW = TextureAddressMode::CLAMP_TO_EDGE;
229
draw2DSamplerLinear_ = draw_->CreateSamplerState(descLinear);
230
}
231
232
if (!draw2DSamplerNearest_) {
233
SamplerStateDesc descNearest{};
234
descNearest.magFilter = TextureFilter::NEAREST;
235
descNearest.minFilter = TextureFilter::NEAREST;
236
descNearest.mipFilter = TextureFilter::NEAREST;
237
descNearest.wrapU = TextureAddressMode::CLAMP_TO_EDGE;
238
descNearest.wrapV = TextureAddressMode::CLAMP_TO_EDGE;
239
descNearest.wrapW = TextureAddressMode::CLAMP_TO_EDGE;
240
draw2DSamplerNearest_ = draw_->CreateSamplerState(descNearest);
241
}
242
}
243
244
Draw2DPipeline *Draw2D::Create2DPipeline(std::function<Draw2DPipelineInfo (ShaderWriter &)> generate) {
245
Ensure2DResources();
246
247
using namespace Draw;
248
const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();
249
250
char *fsCode = new char[8192];
251
ShaderWriterFlags flags = ShaderWriterFlags::NONE;
252
if (gstate_c.Use(GPU_USE_SINGLE_PASS_STEREO)) {
253
flags = ShaderWriterFlags::FS_AUTO_STEREO;
254
}
255
ShaderWriter writer(fsCode, shaderLanguageDesc, ShaderStage::Fragment, Slice<const char *>::empty(), flags);
256
Draw2DPipelineInfo info = generate(writer);
257
_assert_msg_(strlen(fsCode) < 8192, "Draw2D FS length error: %d", (int)strlen(fsCode));
258
259
ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), info.tag);
260
261
_assert_msg_(fs, "Failed to create shader module!\n%s", fsCode);
262
263
// verts have positions in 2D clip coordinates.
264
static const InputLayoutDesc desc = {
265
16,
266
{
267
{ SEM_POSITION, DataFormat::R32G32_FLOAT, 0 },
268
{ SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 8 },
269
},
270
};
271
InputLayout *inputLayout = draw_->CreateInputLayout(desc);
272
273
BlendState *blend = draw_->CreateBlendState({ false, info.writeChannel == RASTER_COLOR ? 0xF : 0x0 });
274
275
DepthStencilStateDesc dsDesc{};
276
if (info.writeChannel == RASTER_DEPTH) {
277
dsDesc.depthTestEnabled = true;
278
dsDesc.depthWriteEnabled = true;
279
dsDesc.depthCompare = Draw::Comparison::ALWAYS;
280
}
281
282
DepthStencilState *depthStencil = draw_->CreateDepthStencilState(dsDesc);
283
RasterState *rasterNoCull = draw_->CreateRasterState({});
284
285
PipelineDesc pipelineDesc{
286
Primitive::TRIANGLE_STRIP,
287
{ draw2DVs_, fs },
288
inputLayout,
289
depthStencil,
290
blend,
291
rasterNoCull,
292
&draw2DUBDesc,
293
info.samplers.is_empty() ? samplers : info.samplers,
294
};
295
296
Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, info.tag);
297
298
fs->Release();
299
300
rasterNoCull->Release();
301
blend->Release();
302
depthStencil->Release();
303
inputLayout->Release();
304
305
return new Draw2DPipeline {
306
pipeline,
307
info,
308
fsCode,
309
};
310
}
311
312
void Draw2D::Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor) {
313
float dX = 1.0f / (float)dstWidth;
314
float dY = 1.0f / (float)dstHeight;
315
float sX = 1.0f / (float)srcWidth;
316
float sY = 1.0f / (float)srcHeight;
317
float xOffset = 0.0f;
318
float yOffset = 0.0f;
319
if (draw_->GetDeviceCaps().requiresHalfPixelOffset) {
320
xOffset = -dX * 0.5f;
321
yOffset = -dY * 0.5f;
322
}
323
Draw2DVertex vtx[4] = {
324
{ -1.0f + 2.0f * dX * dstX1 + xOffset, -(1.0f - 2.0f * dY * dstY1) + yOffset, sX * srcX1, sY * srcY1 },
325
{ -1.0f + 2.0f * dX * dstX2 + xOffset, -(1.0f - 2.0f * dY * dstY1) + yOffset, sX * srcX2, sY * srcY1 },
326
{ -1.0f + 2.0f * dX * dstX1 + xOffset, -(1.0f - 2.0f * dY * dstY2) + yOffset, sX * srcX1, sY * srcY2 },
327
{ -1.0f + 2.0f * dX * dstX2 + xOffset, -(1.0f - 2.0f * dY * dstY2) + yOffset, sX * srcX2, sY * srcY2 },
328
};
329
330
DrawStrip2D(nullptr, vtx, 4, linear, pipeline, srcWidth, srcHeight, scaleFactor);
331
}
332
333
void Draw2D::DrawStrip2D(Draw::Texture *tex, const Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW, float texH, int scaleFactor) {
334
using namespace Draw;
335
336
_dbg_assert_(pipeline);
337
338
if (pipeline->info.writeChannel == RASTER_DEPTH) {
339
_dbg_assert_(draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported);
340
341
// We don't filter inputs when writing depth, results will be bad.
342
linearFilter = false;
343
}
344
345
Draw2DUB ub;
346
ub.texSizeX = tex ? tex->Width() : texW;
347
ub.texSizeY = tex ? tex->Height() : texH;
348
ub.scaleFactor = (float)scaleFactor;
349
350
DepthScaleFactors zScaleFactors = GetDepthScaleFactors(gstate_c.UseFlags());
351
ub.zScale = zScaleFactors.Scale();
352
ub.zScaleInv = 1.0f / ub.zScale;
353
ub.zOffset = zScaleFactors.Offset();
354
355
draw_->BindPipeline(pipeline->pipeline);
356
draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub));
357
358
if (tex) {
359
// This won't work since all the shaders above expect array textures on Vulkan.
360
draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex);
361
}
362
draw_->BindSamplerStates(TEX_SLOT_PSP_TEXTURE, 1, linearFilter ? &draw2DSamplerLinear_ : &draw2DSamplerNearest_);
363
draw_->DrawUP(verts, vertexCount);
364
365
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
366
367
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE | DIRTY_VERTEXSHADER_STATE);
368
}
369
370
Draw2DPipeline *FramebufferManagerCommon::Get2DPipeline(Draw2DShader shader) {
371
using namespace Draw;
372
373
const ShaderLanguageDesc &shaderLanguageDesc = draw_->GetShaderLanguageDesc();
374
375
Draw2DPipeline *pipeline = nullptr;
376
377
switch (shader) {
378
case DRAW2D_COPY_COLOR:
379
if (!draw2DPipelineCopyColor_) {
380
draw2DPipelineCopyColor_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorFs);
381
}
382
pipeline = draw2DPipelineCopyColor_;
383
break;
384
385
case DRAW2D_COPY_COLOR_RECT2LIN:
386
if (!draw2DPipelineColorRect2Lin_) {
387
draw2DPipelineColorRect2Lin_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyColorRect2LinFs);
388
}
389
pipeline = draw2DPipelineColorRect2Lin_;
390
break;
391
case DRAW2D_COPY_DEPTH:
392
if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
393
// Can't do it
394
return nullptr;
395
}
396
if (!draw2DPipelineCopyDepth_) {
397
draw2DPipelineCopyDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2DCopyDepthFs);
398
}
399
pipeline = draw2DPipelineCopyDepth_;
400
break;
401
402
case DRAW2D_ENCODE_R16_TO_DEPTH:
403
if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
404
// Can't do it
405
return nullptr;
406
}
407
if (!draw2DPipelineEncodeDepth_) {
408
draw2DPipelineEncodeDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2DEncodeDepthFs);
409
}
410
pipeline = draw2DPipelineEncodeDepth_;
411
break;
412
413
case DRAW2D_565_TO_DEPTH:
414
if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
415
// Can't do it
416
return nullptr;
417
}
418
if (!draw2DPipeline565ToDepth_) {
419
draw2DPipeline565ToDepth_ = draw2D_.Create2DPipeline(&GenerateDraw2D565ToDepthFs);
420
}
421
pipeline = draw2DPipeline565ToDepth_;
422
break;
423
424
case DRAW2D_565_TO_DEPTH_DESWIZZLE:
425
if (!draw_->GetDeviceCaps().fragmentShaderDepthWriteSupported) {
426
// Can't do it
427
return nullptr;
428
}
429
if (!draw2DPipeline565ToDepthDeswizzle_) {
430
draw2DPipeline565ToDepthDeswizzle_ = draw2D_.Create2DPipeline(&GenerateDraw2D565ToDepthDeswizzleFs);
431
}
432
pipeline = draw2DPipeline565ToDepthDeswizzle_;
433
break;
434
}
435
436
return pipeline;
437
}
438
439