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/PresentationCommon.cpp
Views: 1401
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 <cmath>
19
#include <set>
20
#include <cstdint>
21
#include <algorithm>
22
23
#include "Common/GPU/thin3d.h"
24
25
#include "Common/System/Display.h"
26
#include "Common/System/System.h"
27
#include "Common/System/OSD.h"
28
#include "Common/File/VFS/VFS.h"
29
#include "Common/VR/PPSSPPVR.h"
30
#include "Common/Log.h"
31
#include "Common/TimeUtil.h"
32
#include "Core/Config.h"
33
#include "Core/ConfigValues.h"
34
#include "Core/System.h"
35
#include "Core/HW/Display.h"
36
#include "GPU/Common/PostShader.h"
37
#include "GPU/Common/PresentationCommon.h"
38
#include "GPU/GPUState.h"
39
#include "Common/GPU/ShaderTranslation.h"
40
41
struct Vertex {
42
float x, y, z;
43
float u, v;
44
uint32_t rgba;
45
};
46
47
FRect GetScreenFrame(float pixelWidth, float pixelHeight) {
48
FRect rc = FRect{
49
0.0f,
50
0.0f,
51
pixelWidth,
52
pixelHeight,
53
};
54
55
bool applyInset = !g_Config.bIgnoreScreenInsets;
56
57
if (applyInset) {
58
// Remove the DPI scale to get back to pixels.
59
float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT) / g_display.dpi_scale_x;
60
float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT) / g_display.dpi_scale_x;
61
float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP) / g_display.dpi_scale_y;
62
float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM) / g_display.dpi_scale_y;
63
64
// Adjust left edge to compensate for cutouts (notches) if any.
65
rc.x += left;
66
rc.w -= (left + right);
67
rc.y += top;
68
rc.h -= (top + bottom);
69
}
70
return rc;
71
}
72
73
void CalculateDisplayOutputRect(FRect *rc, float origW, float origH, const FRect &frame, int rotation) {
74
float outW;
75
float outH;
76
77
bool rotated = rotation == ROTATION_LOCKED_VERTICAL || rotation == ROTATION_LOCKED_VERTICAL180;
78
79
bool stretch = g_Config.bDisplayStretch && !g_Config.bDisplayIntegerScale;
80
81
float offsetX = g_Config.fDisplayOffsetX;
82
float offsetY = g_Config.fDisplayOffsetY;
83
84
float scale = g_Config.fDisplayScale;
85
float aspectRatioAdjust = g_Config.fDisplayAspectRatio;
86
87
float origRatio = !rotated ? origW / origH : origH / origW;
88
float frameRatio = frame.w / frame.h;
89
90
if (stretch) {
91
// Automatically set aspect ratio to match the display, IF the rotation matches the output display ratio! Otherwise, just
92
// sets standard aspect ratio because actually stretching will just look silly.
93
bool globalRotated = g_display.rotation == DisplayRotation::ROTATE_90 || g_display.rotation == DisplayRotation::ROTATE_270;
94
if (rotated == g_display.dp_yres > g_display.dp_xres) {
95
origRatio = frameRatio;
96
} else {
97
origRatio *= aspectRatioAdjust;
98
}
99
} else {
100
origRatio *= aspectRatioAdjust;
101
}
102
103
float scaledWidth = frame.w * scale;
104
float scaledHeight = frame.h * scale;
105
106
if (origRatio > frameRatio) {
107
// Image is wider than frame. Center vertically.
108
outW = scaledWidth;
109
outH = scaledWidth / origRatio;
110
} else {
111
// Image is taller than frame. Center horizontally.
112
outW = scaledHeight * origRatio;
113
outH = scaledHeight;
114
}
115
116
// Ye olde 1080p hack: If everything is setup to exactly cover the screen (defaults), and the screen display aspect ratio is 16:9,
117
// cut off one line from the top and bottom.
118
if (scale == 1.0f && aspectRatioAdjust == 1.0f && offsetX == 0.5f && offsetY == 0.5f && !g_Config.bDisplayIntegerScale && g_Config.bDisplayCropTo16x9) {
119
if (fabsf(frame.w / frame.h - 16.0f / 9.0f) < 0.0001f) {
120
outW *= 272.0f / 270.0f;
121
outH *= 272.0f / 270.0f;
122
}
123
}
124
125
if (g_Config.bDisplayIntegerScale) {
126
float wDim = 480.0f;
127
if (rotated) {
128
wDim = 272.0f;
129
}
130
131
int zoom = g_Config.iInternalResolution;
132
if (zoom == 0) {
133
// Auto (1:1) mode, not super meaningful with integer scaling, but let's do something that makes
134
// some sense. use the longest dimension, just to have something. round down.
135
if (!g_Config.IsPortrait()) {
136
zoom = (PSP_CoreParameter().pixelWidth) / 480;
137
} else {
138
zoom = (PSP_CoreParameter().pixelHeight) / 480;
139
}
140
}
141
// If integer scaling, limit ourselves to even multiples of the rendered resolution,
142
// to make sure all the pixels are square.
143
wDim *= zoom;
144
outW = std::max(1.0f, floorf(outW / wDim)) * wDim;
145
outH = outW / origRatio;
146
}
147
148
if (IsVREnabled()) {
149
rc->x = 0;
150
rc->y = 0;
151
rc->w = floorf(frame.w);
152
rc->h = floorf(frame.h);
153
outW = frame.w;
154
outH = frame.h;
155
} else {
156
rc->x = floorf(frame.x + frame.w * offsetX - outW * 0.5f);
157
rc->y = floorf(frame.y + frame.h * offsetY - outH * 0.5f);
158
rc->w = floorf(outW);
159
rc->h = floorf(outH);
160
}
161
}
162
163
PresentationCommon::PresentationCommon(Draw::DrawContext *draw) : draw_(draw) {
164
CreateDeviceObjects();
165
}
166
167
PresentationCommon::~PresentationCommon() {
168
DestroyDeviceObjects();
169
}
170
171
void PresentationCommon::GetCardboardSettings(CardboardSettings *cardboardSettings) const {
172
if (!g_Config.bEnableCardboardVR) {
173
cardboardSettings->enabled = false;
174
return;
175
}
176
177
// Calculate Cardboard Settings
178
float cardboardScreenScale = g_Config.iCardboardScreenSize / 100.0f;
179
float cardboardScreenWidth = pixelWidth_ / 2.0f * cardboardScreenScale;
180
float cardboardScreenHeight = pixelHeight_ * cardboardScreenScale;
181
float cardboardMaxXShift = (pixelWidth_ / 2.0f - cardboardScreenWidth) / 2.0f;
182
float cardboardUserXShift = g_Config.iCardboardXShift / 100.0f * cardboardMaxXShift;
183
float cardboardLeftEyeX = cardboardMaxXShift + cardboardUserXShift;
184
float cardboardRightEyeX = pixelWidth_ / 2.0f + cardboardMaxXShift - cardboardUserXShift;
185
float cardboardMaxYShift = pixelHeight_ / 2.0f - cardboardScreenHeight / 2.0f;
186
float cardboardUserYShift = g_Config.iCardboardYShift / 100.0f * cardboardMaxYShift;
187
float cardboardScreenY = cardboardMaxYShift + cardboardUserYShift;
188
189
cardboardSettings->enabled = true;
190
cardboardSettings->leftEyeXPosition = cardboardLeftEyeX;
191
cardboardSettings->rightEyeXPosition = cardboardRightEyeX;
192
cardboardSettings->screenYPosition = cardboardScreenY;
193
cardboardSettings->screenWidth = cardboardScreenWidth;
194
cardboardSettings->screenHeight = cardboardScreenHeight;
195
}
196
197
static float GetShaderSettingValue(const ShaderInfo *shaderInfo, int i, const char *nameSuffix) {
198
std::string key = shaderInfo->section + nameSuffix;
199
auto it = g_Config.mPostShaderSetting.find(key);
200
if (it != g_Config.mPostShaderSetting.end())
201
return it->second;
202
return shaderInfo->settings[i].value;
203
}
204
205
void PresentationCommon::CalculatePostShaderUniforms(int bufferWidth, int bufferHeight, int targetWidth, int targetHeight, const ShaderInfo *shaderInfo, PostShaderUniforms *uniforms) const {
206
float u_delta = 1.0f / bufferWidth;
207
float v_delta = 1.0f / bufferHeight;
208
float u_pixel_delta = 1.0f / targetWidth;
209
float v_pixel_delta = 1.0f / targetHeight;
210
int flipCount = __DisplayGetFlipCount();
211
int vCount = __DisplayGetVCount();
212
float time[4] = { (float)time_now_d(), (vCount % 60) * 1.0f / 60.0f, (float)vCount, (float)(flipCount % 60) };
213
214
uniforms->texelDelta[0] = u_delta;
215
uniforms->texelDelta[1] = v_delta;
216
uniforms->pixelDelta[0] = u_pixel_delta;
217
uniforms->pixelDelta[1] = v_pixel_delta;
218
memcpy(uniforms->time, time, 4 * sizeof(float));
219
uniforms->timeDelta[0] = time[0] - previousUniforms_.time[0];
220
uniforms->timeDelta[1] = (time[2] - previousUniforms_.time[2]) * (1.0f / 60.0f);
221
uniforms->timeDelta[2] = time[2] - previousUniforms_.time[2];
222
uniforms->timeDelta[3] = time[3] != previousUniforms_.time[3] ? 1.0f : 0.0f;
223
uniforms->video = hasVideo_ ? 1.0f : 0.0f;
224
225
// The shader translator tacks this onto our shaders, if we don't set it they render garbage.
226
uniforms->gl_HalfPixel[0] = u_pixel_delta * 0.5f;
227
uniforms->gl_HalfPixel[1] = v_pixel_delta * 0.5f;
228
229
uniforms->setting[0] = GetShaderSettingValue(shaderInfo, 0, "SettingCurrentValue1");
230
uniforms->setting[1] = GetShaderSettingValue(shaderInfo, 1, "SettingCurrentValue2");
231
uniforms->setting[2] = GetShaderSettingValue(shaderInfo, 2, "SettingCurrentValue3");
232
uniforms->setting[3] = GetShaderSettingValue(shaderInfo, 3, "SettingCurrentValue4");
233
}
234
235
static std::string ReadShaderSrc(const Path &filename) {
236
size_t sz = 0;
237
char *data = (char *)g_VFS.ReadFile(filename.c_str(), &sz);
238
if (!data) {
239
return "";
240
}
241
242
std::string src(data, sz);
243
delete[] data;
244
return src;
245
}
246
247
// Note: called on resize and settings changes.
248
// Also takes care of making sure the appropriate stereo shader is compiled.
249
bool PresentationCommon::UpdatePostShader() {
250
DestroyStereoShader();
251
252
if (gstate_c.Use(GPU_USE_SIMPLE_STEREO_PERSPECTIVE)) {
253
const ShaderInfo *stereoShaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);
254
if (stereoShaderInfo) {
255
bool result = CompilePostShader(stereoShaderInfo, &stereoPipeline_);
256
if (result) {
257
stereoShaderInfo_ = new ShaderInfo(*stereoShaderInfo);
258
}
259
} else {
260
WARN_LOG(Log::G3D, "Failed to get info about stereo shader '%s'", g_Config.sStereoToMonoShader.c_str());
261
}
262
}
263
264
std::vector<const ShaderInfo *> shaderInfo;
265
if (!g_Config.vPostShaderNames.empty()) {
266
ReloadAllPostShaderInfo(draw_);
267
shaderInfo = GetFullPostShadersChain(g_Config.vPostShaderNames);
268
}
269
270
DestroyPostShader();
271
if (shaderInfo.empty()) {
272
usePostShader_ = false;
273
return false;
274
}
275
276
bool usePreviousFrame = false;
277
bool usePreviousAtOutputResolution = false;
278
for (size_t i = 0; i < shaderInfo.size(); ++i) {
279
const ShaderInfo *next = i + 1 < shaderInfo.size() ? shaderInfo[i + 1] : nullptr;
280
Draw::Pipeline *postPipeline = nullptr;
281
if (!BuildPostShader(shaderInfo[i], next, &postPipeline)) {
282
DestroyPostShader();
283
return false;
284
}
285
_dbg_assert_(postPipeline);
286
postShaderPipelines_.push_back(postPipeline);
287
postShaderInfo_.push_back(*shaderInfo[i]);
288
if (shaderInfo[i]->usePreviousFrame) {
289
usePreviousFrame = true;
290
usePreviousAtOutputResolution = shaderInfo[i]->outputResolution;
291
}
292
}
293
294
if (usePreviousFrame) {
295
int w = usePreviousAtOutputResolution ? pixelWidth_ : renderWidth_;
296
int h = usePreviousAtOutputResolution ? pixelHeight_ : renderHeight_;
297
298
_dbg_assert_(w > 0 && h > 0);
299
300
static constexpr int FRAMES = 2;
301
previousFramebuffers_.resize(FRAMES);
302
previousIndex_ = 0;
303
304
for (int i = 0; i < FRAMES; ++i) {
305
previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "inter_presentation" });
306
if (!previousFramebuffers_[i]) {
307
DestroyPostShader();
308
return false;
309
}
310
}
311
}
312
313
usePostShader_ = true;
314
return true;
315
}
316
317
bool PresentationCommon::CompilePostShader(const ShaderInfo *shaderInfo, Draw::Pipeline **outPipeline) const {
318
_assert_(shaderInfo);
319
320
std::string vsSourceGLSL = ReadShaderSrc(shaderInfo->vertexShaderFile);
321
std::string fsSourceGLSL = ReadShaderSrc(shaderInfo->fragmentShaderFile);
322
if (vsSourceGLSL.empty() || fsSourceGLSL.empty()) {
323
return false;
324
}
325
326
std::string vsError;
327
std::string fsError;
328
329
// All post shaders are written in GLSL 1.0 so that's what we pass in here as a "from" language.
330
Draw::ShaderModule *vs = CompileShaderModule(ShaderStage::Vertex, GLSL_1xx, vsSourceGLSL, &vsError);
331
Draw::ShaderModule *fs = CompileShaderModule(ShaderStage::Fragment, GLSL_1xx, fsSourceGLSL, &fsError);
332
333
// Don't worry, CompileShaderModule makes sure they get freed if one succeeded.
334
if (!fs || !vs) {
335
std::string errorString = vsError + "\n" + fsError;
336
// DO NOT turn this into an ERROR_LOG_REPORT, as it will pollute our logs with all kinds of
337
// user shader experiments.
338
ERROR_LOG(Log::FrameBuf, "Failed to build post-processing program from %s and %s!\n%s", shaderInfo->vertexShaderFile.c_str(), shaderInfo->fragmentShaderFile.c_str(), errorString.c_str());
339
ShowPostShaderError(errorString);
340
return false;
341
}
342
343
UniformBufferDesc postShaderDesc{ sizeof(PostShaderUniforms), {
344
{ "gl_HalfPixel", 0, -1, UniformType::FLOAT4, offsetof(PostShaderUniforms, gl_HalfPixel) },
345
{ "u_texelDelta", 1, 1, UniformType::FLOAT2, offsetof(PostShaderUniforms, texelDelta) },
346
{ "u_pixelDelta", 2, 2, UniformType::FLOAT2, offsetof(PostShaderUniforms, pixelDelta) },
347
{ "u_time", 3, 3, UniformType::FLOAT4, offsetof(PostShaderUniforms, time) },
348
{ "u_timeDelta", 4, 4, UniformType::FLOAT4, offsetof(PostShaderUniforms, timeDelta) },
349
{ "u_setting", 5, 5, UniformType::FLOAT4, offsetof(PostShaderUniforms, setting) },
350
{ "u_video", 6, 6, UniformType::FLOAT1, offsetof(PostShaderUniforms, video) },
351
} };
352
353
Draw::Pipeline *pipeline = CreatePipeline({ vs, fs }, true, &postShaderDesc);
354
355
fs->Release();
356
vs->Release();
357
358
if (!pipeline)
359
return false;
360
361
*outPipeline = pipeline;
362
return true;
363
}
364
365
bool PresentationCommon::BuildPostShader(const ShaderInfo * shaderInfo, const ShaderInfo * next, Draw::Pipeline **outPipeline) {
366
if (!CompilePostShader(shaderInfo, outPipeline)) {
367
return false;
368
}
369
370
if (!shaderInfo->outputResolution || next) {
371
int nextWidth = renderWidth_;
372
int nextHeight = renderHeight_;
373
374
// When chaining, we use the previous resolution as a base, rather than the render resolution.
375
if (!postShaderFramebuffers_.empty())
376
draw_->GetFramebufferDimensions(postShaderFramebuffers_.back(), &nextWidth, &nextHeight);
377
378
if (next && next->isUpscalingFilter) {
379
// Force 1x for this shader, so the next can upscale.
380
const bool isPortrait = g_Config.IsPortrait();
381
nextWidth = isPortrait ? 272 : 480;
382
nextHeight = isPortrait ? 480 : 272;
383
} else if (next && next->SSAAFilterLevel >= 2) {
384
// Increase the resolution this shader outputs for the next to SSAA.
385
nextWidth *= next->SSAAFilterLevel;
386
nextHeight *= next->SSAAFilterLevel;
387
} else if (shaderInfo->outputResolution) {
388
// If the current shader uses output res (not next), we will use output res for it.
389
FRect rc;
390
FRect frame = GetScreenFrame((float)pixelWidth_, (float)pixelHeight_);
391
CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, frame, g_Config.iInternalScreenRotation);
392
nextWidth = (int)rc.w;
393
nextHeight = (int)rc.h;
394
}
395
396
if (!AllocateFramebuffer(nextWidth, nextHeight)) {
397
(*outPipeline)->Release();
398
*outPipeline = nullptr;
399
return false;
400
}
401
}
402
403
return true;
404
}
405
406
bool PresentationCommon::AllocateFramebuffer(int w, int h) {
407
using namespace Draw;
408
409
// First, let's try to find a framebuffer of the right size that is NOT the most recent.
410
Framebuffer *last = postShaderFramebuffers_.empty() ? nullptr : postShaderFramebuffers_.back();
411
for (const auto &prev : postShaderFBOUsage_) {
412
if (prev.w == w && prev.h == h && prev.fbo != last) {
413
// Great, this one's perfect. Ref it for when we release.
414
prev.fbo->AddRef();
415
postShaderFramebuffers_.push_back(prev.fbo);
416
return true;
417
}
418
}
419
420
// No depth/stencil for post processing
421
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, 0, false, "presentation" });
422
if (!fbo) {
423
return false;
424
}
425
426
postShaderFBOUsage_.push_back({ fbo, w, h });
427
postShaderFramebuffers_.push_back(fbo);
428
return true;
429
}
430
431
void PresentationCommon::ShowPostShaderError(const std::string &errorString) {
432
// let's show the first line of the error string as an OSM.
433
std::set<std::string> blacklistedLines;
434
// These aren't useful to show, skip to the first interesting line.
435
blacklistedLines.insert("Fragment shader failed to compile with the following errors:");
436
blacklistedLines.insert("Vertex shader failed to compile with the following errors:");
437
blacklistedLines.insert("Compile failed.");
438
blacklistedLines.insert("");
439
440
std::string firstLine;
441
size_t start = 0;
442
for (size_t i = 0; i < errorString.size(); i++) {
443
if (errorString[i] == '\n' && i == start) {
444
start = i + 1;
445
} else if (errorString[i] == '\n') {
446
firstLine = errorString.substr(start, i - start);
447
if (blacklistedLines.find(firstLine) == blacklistedLines.end()) {
448
break;
449
}
450
start = i + 1;
451
firstLine.clear();
452
}
453
}
454
if (!firstLine.empty()) {
455
g_OSD.Show(OSDType::MESSAGE_ERROR_DUMP, "Post-shader error: " + firstLine + "...:\n" + errorString, 10.0f);
456
} else {
457
g_OSD.Show(OSDType::MESSAGE_ERROR, "Post-shader error, see log for details", 10.0f);
458
}
459
}
460
461
void PresentationCommon::DeviceLost() {
462
DestroyDeviceObjects();
463
draw_ = nullptr;
464
}
465
466
void PresentationCommon::DeviceRestore(Draw::DrawContext *draw) {
467
draw_ = draw;
468
CreateDeviceObjects();
469
}
470
471
Draw::Pipeline *PresentationCommon::CreatePipeline(std::vector<Draw::ShaderModule *> shaders, bool postShader, const UniformBufferDesc *uniformDesc) const {
472
using namespace Draw;
473
474
Semantic pos = SEM_POSITION;
475
Semantic tc = SEM_TEXCOORD0;
476
// Shader translation marks these both as "TEXCOORDs" on HLSL...
477
if (postShader && (lang_ == HLSL_D3D11 || lang_ == HLSL_D3D9)) {
478
pos = SEM_TEXCOORD0;
479
tc = SEM_TEXCOORD1;
480
}
481
482
// TODO: Maybe get rid of color0.
483
InputLayoutDesc inputDesc = {
484
sizeof(Vertex),
485
{
486
{ pos, DataFormat::R32G32B32_FLOAT, 0 },
487
{ tc, DataFormat::R32G32_FLOAT, 12 },
488
{ SEM_COLOR0, DataFormat::R8G8B8A8_UNORM, 20 },
489
},
490
};
491
492
InputLayout *inputLayout = draw_->CreateInputLayout(inputDesc);
493
DepthStencilState *depth = draw_->CreateDepthStencilState({ false, false, Comparison::LESS });
494
BlendState *blendstateOff = draw_->CreateBlendState({ false, 0xF });
495
RasterState *rasterNoCull = draw_->CreateRasterState({});
496
497
PipelineDesc pipelineDesc{ Primitive::TRIANGLE_STRIP, shaders, inputLayout, depth, blendstateOff, rasterNoCull, uniformDesc };
498
Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, "presentation");
499
500
inputLayout->Release();
501
depth->Release();
502
blendstateOff->Release();
503
rasterNoCull->Release();
504
505
return pipeline;
506
}
507
508
void PresentationCommon::CreateDeviceObjects() {
509
using namespace Draw;
510
_assert_(vdata_ == nullptr);
511
512
// TODO: Could probably just switch to DrawUP, it's supported well by all backends now.
513
vdata_ = draw_->CreateBuffer(sizeof(Vertex) * 12, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA);
514
515
samplerNearest_ = draw_->CreateSamplerState({ TextureFilter::NEAREST, TextureFilter::NEAREST, TextureFilter::NEAREST, 0.0f, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE });
516
samplerLinear_ = draw_->CreateSamplerState({ TextureFilter::LINEAR, TextureFilter::LINEAR, TextureFilter::LINEAR, 0.0f, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE });
517
518
texColor_ = CreatePipeline({ draw_->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw_->GetFshaderPreset(FS_TEXTURE_COLOR_2D) }, false, &vsTexColBufDesc);
519
texColorRBSwizzle_ = CreatePipeline({ draw_->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw_->GetFshaderPreset(FS_TEXTURE_COLOR_2D_RB_SWIZZLE) }, false, &vsTexColBufDesc);
520
521
if (restorePostShader_)
522
UpdatePostShader();
523
restorePostShader_ = false;
524
}
525
526
template <typename T>
527
static void DoRelease(T *&obj) {
528
if (obj)
529
obj->Release();
530
obj = nullptr;
531
}
532
533
template <typename T>
534
static void DoReleaseVector(std::vector<T *> &list) {
535
for (auto &obj : list)
536
obj->Release();
537
list.clear();
538
}
539
540
void PresentationCommon::DestroyDeviceObjects() {
541
DoRelease(texColor_);
542
DoRelease(texColorRBSwizzle_);
543
DoRelease(samplerNearest_);
544
DoRelease(samplerLinear_);
545
DoRelease(vdata_);
546
DoRelease(srcTexture_);
547
DoRelease(srcFramebuffer_);
548
549
restorePostShader_ = usePostShader_;
550
DestroyPostShader();
551
DestroyStereoShader();
552
}
553
554
void PresentationCommon::DestroyPostShader() {
555
usePostShader_ = false;
556
557
DoReleaseVector(postShaderPipelines_);
558
DoReleaseVector(postShaderFramebuffers_);
559
DoReleaseVector(previousFramebuffers_);
560
postShaderInfo_.clear();
561
postShaderFBOUsage_.clear();
562
}
563
564
void PresentationCommon::DestroyStereoShader() {
565
DoRelease(stereoPipeline_);
566
delete stereoShaderInfo_;
567
stereoShaderInfo_ = nullptr;
568
}
569
570
Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, ShaderLanguage lang, const std::string &src, std::string *errorString) const {
571
std::string translated = src;
572
if (lang != lang_) {
573
// Gonna have to upconvert the shader.
574
if (!TranslateShader(&translated, lang_, draw_->GetShaderLanguageDesc(), nullptr, src, lang, stage, errorString)) {
575
ERROR_LOG(Log::FrameBuf, "Failed to translate post-shader. Error string: '%s'\nSource code:\n%s\n", errorString->c_str(), src.c_str());
576
return nullptr;
577
}
578
}
579
Draw::ShaderModule *shader = draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
580
return shader;
581
}
582
583
void PresentationCommon::SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight) {
584
// AddRef before release and assign in case it's the same.
585
texture->AddRef();
586
587
DoRelease(srcTexture_);
588
DoRelease(srcFramebuffer_);
589
590
srcTexture_ = texture;
591
srcWidth_ = bufferWidth;
592
srcHeight_ = bufferHeight;
593
}
594
595
void PresentationCommon::SourceFramebuffer(Draw::Framebuffer *fb, int bufferWidth, int bufferHeight) {
596
fb->AddRef();
597
598
DoRelease(srcTexture_);
599
DoRelease(srcFramebuffer_);
600
601
srcFramebuffer_ = fb;
602
srcWidth_ = bufferWidth;
603
srcHeight_ = bufferHeight;
604
}
605
606
// Return value is if stereo binding succeeded.
607
bool PresentationCommon::BindSource(int binding, bool bindStereo) {
608
if (srcTexture_) {
609
draw_->BindTexture(binding, srcTexture_);
610
return false;
611
} else if (srcFramebuffer_) {
612
if (bindStereo) {
613
if (srcFramebuffer_->Layers() > 1) {
614
draw_->BindFramebufferAsTexture(srcFramebuffer_, binding, Draw::FB_COLOR_BIT, Draw::ALL_LAYERS);
615
return true;
616
} else {
617
// Single layer. This might be from a post shader and those don't yet support stereo.
618
draw_->BindFramebufferAsTexture(srcFramebuffer_, binding, Draw::FB_COLOR_BIT, 0);
619
return false;
620
}
621
} else {
622
draw_->BindFramebufferAsTexture(srcFramebuffer_, binding, Draw::FB_COLOR_BIT, 0);
623
return false;
624
}
625
} else {
626
_assert_(false);
627
return false;
628
}
629
}
630
631
void PresentationCommon::UpdateUniforms(bool hasVideo) {
632
hasVideo_ = hasVideo;
633
}
634
635
void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) {
636
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
637
638
// TODO: If shader objects have been created by now, we might have received errors.
639
// GLES can have the shader fail later, shader->failed / shader->error.
640
// This should auto-disable usePostShader_ and call ShowPostShaderError().
641
642
bool useNearest = flags & OutputFlags::NEAREST;
643
bool useStereo = gstate_c.Use(GPU_USE_SIMPLE_STEREO_PERSPECTIVE) && stereoPipeline_ != nullptr; // TODO: Also check that the backend has support for it.
644
645
const bool usePostShader = usePostShader_ && !useStereo && !(flags & OutputFlags::RB_SWIZZLE);
646
const bool isFinalAtOutputResolution = usePostShader && postShaderFramebuffers_.size() < postShaderPipelines_.size();
647
Draw::Framebuffer *postShaderOutput = nullptr;
648
int lastWidth = srcWidth_;
649
int lastHeight = srcHeight_;
650
651
int pixelWidth = pixelWidth_;
652
int pixelHeight = pixelHeight_;
653
654
// These are the output coordinates.
655
FRect frame = GetScreenFrame((float)pixelWidth, (float)pixelHeight);
656
// Note: In cardboard mode, we halve the width here to compensate
657
// for splitting the window in half, while still reusing normal centering.
658
if (g_Config.bEnableCardboardVR) {
659
frame.w /= 2.0;
660
pixelWidth /= 2;
661
}
662
FRect rc;
663
CalculateDisplayOutputRect(&rc, 480.0f, 272.0f, frame, uvRotation);
664
665
if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
666
rc.x -= 0.5f;
667
// This is plus because the top is larger y.
668
rc.y += 0.5f;
669
}
670
671
// To make buffer updates easier, we use one array of verts.
672
int postVertsOffset = (int)sizeof(Vertex) * 4;
673
674
float finalU0 = u0, finalU1 = u1, finalV0 = v0, finalV1 = v1;
675
676
if (usePostShader && !(isFinalAtOutputResolution && postShaderPipelines_.size() == 1)) {
677
// The final blit will thus use the full texture.
678
finalU0 = 0.0f;
679
finalV0 = 0.0f;
680
finalU1 = 1.0f;
681
finalV1 = 1.0f;
682
}
683
684
// Our vertex buffer is split into three parts, with four vertices each:
685
// 0-3: The final blit vertices (needs to handle cropping the input ONLY if post-processing is not enabled)
686
// 4-7: Post-processing, other passes
687
// 8-11: Post-processing, first pass (needs to handle cropping the input image, if wrong dimensions)
688
Vertex verts[12] = {
689
{ rc.x, rc.y, 0, finalU0, finalV0, 0xFFFFFFFF }, // TL
690
{ rc.x + rc.w, rc.y, 0, finalU1, finalV0, 0xFFFFFFFF }, // TR
691
{ rc.x, rc.y + rc.h, 0, finalU0, finalV1, 0xFFFFFFFF }, // BL
692
{ rc.x + rc.w, rc.y + rc.h, 0, finalU1, finalV1, 0xFFFFFFFF }, // BR
693
};
694
695
// Rescale X, Y to normalized coordinate system.
696
float invDestW = 2.0f / pixelWidth;
697
float invDestH = 2.0f / pixelHeight;
698
for (int i = 0; i < 4; i++) {
699
verts[i].x = verts[i].x * invDestW - 1.0f;
700
verts[i].y = verts[i].y * invDestH - 1.0f;
701
}
702
703
if (uvRotation != ROTATION_LOCKED_HORIZONTAL) {
704
struct {
705
float u;
706
float v;
707
} temp[4];
708
int rotation = 0;
709
// Vertical and Vertical180 needed swapping after we changed the coordinate system.
710
switch (uvRotation) {
711
case ROTATION_LOCKED_HORIZONTAL180: rotation = 2; break;
712
case ROTATION_LOCKED_VERTICAL: rotation = 3; break;
713
case ROTATION_LOCKED_VERTICAL180: rotation = 1; break;
714
}
715
716
// If we flipped, we rotate the other way.
717
if ((flags & OutputFlags::BACKBUFFER_FLIPPED) || (flags & OutputFlags::POSITION_FLIPPED)) {
718
if ((rotation & 1) != 0)
719
rotation ^= 2;
720
}
721
722
static int rotLookup[4] = { 0, 1, 3, 2 };
723
724
for (int i = 0; i < 4; i++) {
725
int otherI = rotLookup[(rotLookup[i] + rotation) & 3];
726
temp[i].u = verts[otherI].u;
727
temp[i].v = verts[otherI].v;
728
}
729
for (int i = 0; i < 4; i++) {
730
verts[i].u = temp[i].u;
731
verts[i].v = temp[i].v;
732
}
733
}
734
735
if (isFinalAtOutputResolution || useStereo) {
736
// In this mode, we ignore the g_display_rot_matrix. Apply manually.
737
if (g_display.rotation != DisplayRotation::ROTATE_0) {
738
for (int i = 0; i < 4; i++) {
739
Lin::Vec3 v(verts[i].x, verts[i].y, verts[i].z);
740
// Backwards notation, should fix that...
741
v = v * g_display.rot_matrix;
742
verts[i].x = v.x;
743
verts[i].y = v.y;
744
}
745
}
746
}
747
748
if (flags & OutputFlags::PILLARBOX) {
749
for (int i = 0; i < 4; i++) {
750
// Looks about right.
751
verts[i].x *= 0.75f;
752
}
753
}
754
755
// Finally, we compensate the y vertex positions for the backbuffer for any flipping.
756
if ((flags & OutputFlags::POSITION_FLIPPED) || (flags & OutputFlags::BACKBUFFER_FLIPPED)) {
757
for (int i = 0; i < 4; i++) {
758
verts[i].y = -verts[i].y;
759
}
760
}
761
762
// Grab the previous framebuffer early so we can change previousIndex_ when we want.
763
Draw::Framebuffer *previousFramebuffer = previousFramebuffers_.empty() ? nullptr : previousFramebuffers_[previousIndex_];
764
765
PostShaderUniforms uniforms;
766
const auto performShaderPass = [&](const ShaderInfo *shaderInfo, Draw::Framebuffer *postShaderFramebuffer, Draw::Pipeline *postShaderPipeline, int vertsOffset) {
767
if (postShaderOutput) {
768
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT, 0);
769
} else {
770
BindSource(0, false);
771
}
772
BindSource(1, false);
773
if (shaderInfo->usePreviousFrame)
774
draw_->BindFramebufferAsTexture(previousFramebuffer, 2, Draw::FB_COLOR_BIT, 0);
775
776
int nextWidth, nextHeight;
777
draw_->GetFramebufferDimensions(postShaderFramebuffer, &nextWidth, &nextHeight);
778
Draw::Viewport viewport{ 0, 0, (float)nextWidth, (float)nextHeight, 0.0f, 1.0f };
779
draw_->SetViewport(viewport);
780
draw_->SetScissorRect(0, 0, nextWidth, nextHeight);
781
782
CalculatePostShaderUniforms(lastWidth, lastHeight, nextWidth, nextHeight, shaderInfo, &uniforms);
783
784
draw_->BindPipeline(postShaderPipeline);
785
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
786
787
Draw::SamplerState *sampler = useNearest || shaderInfo->isUpscalingFilter ? samplerNearest_ : samplerLinear_;
788
draw_->BindSamplerStates(0, 1, &sampler);
789
draw_->BindSamplerStates(1, 1, &sampler);
790
if (shaderInfo->usePreviousFrame)
791
draw_->BindSamplerStates(2, 1, &sampler);
792
793
draw_->BindVertexBuffer(vdata_, vertsOffset);
794
draw_->Draw(4, 0);
795
796
postShaderOutput = postShaderFramebuffer;
797
lastWidth = nextWidth;
798
lastHeight = nextHeight;
799
};
800
801
if (usePostShader) {
802
// When we render to temp framebuffers during post, we switch position, not UV.
803
// The flipping here is only because D3D has a clip coordinate system that doesn't match their screen coordinate system.
804
// The flipping here is only because D3D has a clip coordinate system that doesn't match their screen coordinate system.
805
bool flipped = flags & OutputFlags::POSITION_FLIPPED;
806
float y0 = flipped ? 1.0f : -1.0f;
807
float y1 = flipped ? -1.0f : 1.0f;
808
verts[4] = { -1.0f, y0, 0.0f, 0.0f, 0.0f, 0xFFFFFFFF }; // TL
809
verts[5] = { 1.0f, y0, 0.0f, 1.0f, 0.0f, 0xFFFFFFFF }; // TR
810
verts[6] = { -1.0f, y1, 0.0f, 0.0f, 1.0f, 0xFFFFFFFF }; // BL
811
verts[7] = { 1.0f, y1, 0.0f, 1.0f, 1.0f, 0xFFFFFFFF }; // BR
812
813
// Now, adjust for the desired input rectangle.
814
verts[8] = { -1.0f, y0, 0.0f, u0, v0, 0xFFFFFFFF }; // TL
815
verts[9] = { 1.0f, y0, 0.0f, u1, v0, 0xFFFFFFFF }; // TR
816
verts[10] = { -1.0f, y1, 0.0f, u0, v1, 0xFFFFFFFF }; // BL
817
verts[11] = { 1.0f, y1, 0.0f, u1, v1, 0xFFFFFFFF }; // BR
818
819
draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, sizeof(verts), Draw::UPDATE_DISCARD);
820
821
for (size_t i = 0; i < postShaderFramebuffers_.size(); ++i) {
822
Draw::Pipeline *postShaderPipeline = postShaderPipelines_[i];
823
const ShaderInfo *shaderInfo = &postShaderInfo_[i];
824
Draw::Framebuffer *postShaderFramebuffer = postShaderFramebuffers_[i];
825
if (!isFinalAtOutputResolution && i == postShaderFramebuffers_.size() - 1 && !previousFramebuffers_.empty()) {
826
// This is the last pass and we're going direct to the backbuffer after this.
827
// Redirect output to a separate framebuffer to keep the previous frame.
828
previousIndex_++;
829
if (previousIndex_ >= (int)previousFramebuffers_.size())
830
previousIndex_ = 0;
831
postShaderFramebuffer = previousFramebuffers_[previousIndex_];
832
}
833
834
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PostShader");
835
836
// Pick vertices 8-11 for the first pass.
837
int vertOffset = i == 0 ? (int)sizeof(Vertex) * 8 : (int)sizeof(Vertex) * 4;
838
performShaderPass(shaderInfo, postShaderFramebuffer, postShaderPipeline, vertOffset);
839
}
840
841
if (isFinalAtOutputResolution && postShaderInfo_.back().isUpscalingFilter)
842
useNearest = true;
843
} else {
844
// Only need to update the first four verts, the rest are unused.
845
draw_->UpdateBuffer(vdata_, (const uint8_t *)verts, 0, postVertsOffset, Draw::UPDATE_DISCARD);
846
}
847
848
// If we need to save the previous frame, we have to save any final pass in a framebuffer.
849
if (isFinalAtOutputResolution && !previousFramebuffers_.empty()) {
850
Draw::Pipeline *postShaderPipeline = postShaderPipelines_.back();
851
const ShaderInfo *shaderInfo = &postShaderInfo_.back();
852
853
// Pick the next to render to.
854
previousIndex_++;
855
if (previousIndex_ >= (int)previousFramebuffers_.size())
856
previousIndex_ = 0;
857
Draw::Framebuffer *postShaderFramebuffer = previousFramebuffers_[previousIndex_];
858
859
draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "InterFrameBlit");
860
performShaderPass(shaderInfo, postShaderFramebuffer, postShaderPipeline, postVertsOffset);
861
}
862
863
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "FinalBlit");
864
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
865
866
Draw::Pipeline *pipeline = (flags & OutputFlags::RB_SWIZZLE) ? texColorRBSwizzle_ : texColor_;
867
868
if (useStereo) {
869
draw_->BindPipeline(stereoPipeline_);
870
if (!BindSource(0, true)) {
871
// Fall back
872
draw_->BindPipeline(texColor_);
873
useStereo = false; // Otherwise we end up uploading the wrong uniforms
874
}
875
} else {
876
if (isFinalAtOutputResolution && previousFramebuffers_.empty()) {
877
pipeline = postShaderPipelines_.back();
878
}
879
880
draw_->BindPipeline(pipeline);
881
if (postShaderOutput) {
882
draw_->BindFramebufferAsTexture(postShaderOutput, 0, Draw::FB_COLOR_BIT, 0);
883
} else {
884
BindSource(0, false);
885
}
886
}
887
BindSource(1, false);
888
889
if (isFinalAtOutputResolution && previousFramebuffers_.empty()) {
890
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc.w, (int)rc.h, &postShaderInfo_.back(), &uniforms);
891
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
892
} else if (useStereo) {
893
CalculatePostShaderUniforms(lastWidth, lastHeight, (int)rc.w, (int)rc.h, stereoShaderInfo_, &uniforms);
894
draw_->UpdateDynamicUniformBuffer(&uniforms, sizeof(uniforms));
895
} else {
896
Draw::VsTexColUB ub{};
897
memcpy(ub.WorldViewProj, g_display.rot_matrix.m, sizeof(float) * 16);
898
draw_->UpdateDynamicUniformBuffer(&ub, sizeof(ub));
899
}
900
901
draw_->BindVertexBuffer(vdata_, 0);
902
903
Draw::SamplerState *sampler = useNearest ? samplerNearest_ : samplerLinear_;
904
draw_->BindSamplerStates(0, 1, &sampler);
905
draw_->BindSamplerStates(1, 1, &sampler);
906
907
auto setViewport = [&](float x, float y, float w, float h) {
908
Draw::Viewport viewport{ x, y, w, h, 0.0f, 1.0f };
909
draw_->SetViewport(viewport);
910
};
911
912
CardboardSettings cardboardSettings;
913
GetCardboardSettings(&cardboardSettings);
914
if (cardboardSettings.enabled) {
915
// TODO: This could actually support stereo now, with an appropriate shader.
916
917
// This is what the left eye sees.
918
setViewport(cardboardSettings.leftEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
919
draw_->Draw(4, 0);
920
921
// And this is the right eye, unless they're a pirate.
922
setViewport(cardboardSettings.rightEyeXPosition, cardboardSettings.screenYPosition, cardboardSettings.screenWidth, cardboardSettings.screenHeight);
923
draw_->Draw(4, 0);
924
} else {
925
setViewport(0.0f, 0.0f, (float)pixelWidth_, (float)pixelHeight_);
926
draw_->Draw(4, 0);
927
}
928
929
DoRelease(srcFramebuffer_);
930
DoRelease(srcTexture_);
931
932
// Unbinds all textures and samplers too, needed since sometimes a MakePixelTexture is deleted etc.
933
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
934
935
previousUniforms_ = uniforms;
936
presentedThisFrame_ = true;
937
}
938
939
void PresentationCommon::CalculateRenderResolution(int *width, int *height, int *scaleFactor, bool *upscaling, bool *ssaa) const {
940
// Check if postprocessing shader is doing upscaling as it requires native resolution
941
std::vector<const ShaderInfo *> shaderInfo;
942
if (!g_Config.vPostShaderNames.empty()) {
943
ReloadAllPostShaderInfo(draw_);
944
RemoveUnknownPostShaders(&g_Config.vPostShaderNames);
945
FixPostShaderOrder(&g_Config.vPostShaderNames);
946
shaderInfo = GetFullPostShadersChain(g_Config.vPostShaderNames);
947
}
948
949
bool firstIsUpscalingFilter = shaderInfo.empty() ? false : shaderInfo.front()->isUpscalingFilter;
950
int firstSSAAFilterLevel = shaderInfo.empty() ? 0 : shaderInfo.front()->SSAAFilterLevel;
951
952
// In auto mode (zoom == 0), round up to an integer zoom factor for the render size.
953
int zoom = g_Config.iInternalResolution;
954
if (zoom == 0 || firstSSAAFilterLevel >= 2) {
955
// auto mode, use the longest dimension
956
if (!g_Config.IsPortrait()) {
957
zoom = (PSP_CoreParameter().pixelWidth + 479) / 480;
958
} else {
959
zoom = (PSP_CoreParameter().pixelHeight + 479) / 480;
960
}
961
if (firstSSAAFilterLevel >= 2)
962
zoom *= firstSSAAFilterLevel;
963
}
964
if (zoom <= 1 || firstIsUpscalingFilter)
965
zoom = 1;
966
967
if (upscaling) {
968
*upscaling = firstIsUpscalingFilter;
969
for (auto &info : shaderInfo) {
970
*upscaling = *upscaling || info->isUpscalingFilter;
971
}
972
}
973
if (ssaa) {
974
*ssaa = firstSSAAFilterLevel >= 2;
975
for (auto &info : shaderInfo) {
976
*ssaa = *ssaa || info->SSAAFilterLevel >= 2;
977
}
978
}
979
980
if (IsVREnabled()) {
981
*width = 480 * zoom;
982
*height = 480 * zoom;
983
} else {
984
// Note: We previously checked g_Config.IsPortrait (internal rotation) here but that was wrong -
985
// we still render at 480x272 * zoom.
986
*width = 480 * zoom;
987
*height = 272 * zoom;
988
}
989
990
*scaleFactor = zoom;
991
}
992
993