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