CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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