Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iota97's "Motion blur" - LCD persistence shader, plus fixes to make it work with OpenGL #16531

Merged
merged 6 commits into from
Dec 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Common/GPU/OpenGL/GLRenderManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,23 @@ struct GLRProgramFlags {
bool useClipDistance2 : 1;
};

// Unless you manage lifetimes in some smart way,
// your loc data for uniforms and samplers need to be in a struct
// derived from this, and passed into CreateProgram.
class GLRProgramLocData {
public:
virtual ~GLRProgramLocData() {}
};

class GLRProgram {
public:
~GLRProgram() {
if (program) {
glDeleteProgram(program);
}
if (locData_) {
delete locData_;
}
}
struct Semantic {
int location;
Expand All @@ -127,6 +138,8 @@ class GLRProgram {
std::vector<Semantic> semantics_;
std::vector<UniformLocQuery> queries_;
std::vector<Initializer> initialize_;

GLRProgramLocData *locData_;
bool use_clip_distance[8]{};

struct UniformInfo {
Expand All @@ -148,6 +161,7 @@ class GLRProgram {
}
return loc;
}

std::unordered_map<std::string, UniformInfo> uniformCache_;
};

Expand Down Expand Up @@ -438,13 +452,14 @@ class GLRenderManager {
// not be an active render pass.
GLRProgram *CreateProgram(
std::vector<GLRShader *> shaders, std::vector<GLRProgram::Semantic> semantics, std::vector<GLRProgram::UniformLocQuery> queries,
std::vector<GLRProgram::Initializer> initializers, const GLRProgramFlags &flags) {
std::vector<GLRProgram::Initializer> initializers, GLRProgramLocData *locData, const GLRProgramFlags &flags) {
GLRInitStep step{ GLRInitStepType::CREATE_PROGRAM };
_assert_(shaders.size() <= ARRAY_SIZE(step.create_program.shaders));
step.create_program.program = new GLRProgram();
step.create_program.program->semantics_ = semantics;
step.create_program.program->queries_ = queries;
step.create_program.program->initialize_ = initializers;
step.create_program.program->locData_ = locData;
step.create_program.program->use_clip_distance[0] = flags.useClipDistance0;
step.create_program.program->use_clip_distance[1] = flags.useClipDistance1;
step.create_program.program->use_clip_distance[2] = flags.useClipDistance2;
Expand Down
46 changes: 30 additions & 16 deletions Common/GPU/OpenGL/thin3d_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ bool OpenGLShaderModule::Compile(GLRenderManager *render, ShaderLanguage languag
return true;
}

struct PipelineLocData : GLRProgramLocData {
GLint samplerLocs_[MAX_TEXTURE_SLOTS]{};
std::vector<GLint> dynamicUniformLocs_;
};

class OpenGLInputLayout : public InputLayout {
public:
OpenGLInputLayout(GLRenderManager *render) : render_(render) {}
Expand All @@ -285,9 +290,10 @@ class OpenGLPipeline : public Pipeline {
if (program_) {
render_->DeleteProgram(program_);
}
// DO NOT delete locs_ here, it's deleted by the render manager.
}

bool LinkShaders();
bool LinkShaders(const PipelineDesc &desc);

GLuint prim = 0;
std::vector<OpenGLShaderModule *> shaders;
Expand All @@ -296,12 +302,14 @@ class OpenGLPipeline : public Pipeline {
AutoRef<OpenGLBlendState> blend;
AutoRef<OpenGLRasterState> raster;

// Not owned!
PipelineLocData *locs_ = nullptr;

// TODO: Optimize by getting the locations first and putting in a custom struct
UniformBufferDesc dynamicUniforms;
GLint samplerLocs_[MAX_TEXTURE_SLOTS]{};
std::vector<GLint> dynamicUniformLocs_;
GLRProgram *program_ = nullptr;


// Allow using other sampler names than sampler0, sampler1 etc in shaders.
// If not set, will default to those, though.
Slice<SamplerDef> samplers_;
Expand Down Expand Up @@ -1133,10 +1141,10 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const
}
if (desc.uniformDesc) {
pipeline->dynamicUniforms = *desc.uniformDesc;
pipeline->dynamicUniformLocs_.resize(desc.uniformDesc->uniforms.size());
}

pipeline->samplers_ = desc.samplers;
if (pipeline->LinkShaders()) {
if (pipeline->LinkShaders(desc)) {
// Build the rest of the virtual pipeline object.
pipeline->prim = primToGL[(int)desc.prim];
pipeline->depthStencil = (OpenGLDepthStencilState *)desc.depthStencil;
Expand Down Expand Up @@ -1206,7 +1214,7 @@ ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguag
}
}

bool OpenGLPipeline::LinkShaders() {
bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {
std::vector<GLRShader *> linkShaders;
for (auto shaderModule : shaders) {
if (shaderModule) {
Expand Down Expand Up @@ -1237,32 +1245,38 @@ bool OpenGLPipeline::LinkShaders() {
semantics.push_back({ SEM_POSITION, "a_position" });
semantics.push_back({ SEM_TEXCOORD0, "a_texcoord0" });

locs_ = new PipelineLocData();
locs_->dynamicUniformLocs_.resize(desc.uniformDesc->uniforms.size());

std::vector<GLRProgram::UniformLocQuery> queries;
int samplersToCheck;
if (!samplers_.is_empty()) {
for (int i = 0; i < (int)std::min((const uint32_t)samplers_.size(), MAX_TEXTURE_SLOTS); i++) {
queries.push_back({ &samplerLocs_[i], samplers_[i].name, true });
queries.push_back({ &locs_->samplerLocs_[i], samplers_[i].name, true });
}
samplersToCheck = (int)samplers_.size();
} else {
queries.push_back({ &samplerLocs_[0], "sampler0" });
queries.push_back({ &samplerLocs_[1], "sampler1" });
queries.push_back({ &samplerLocs_[2], "sampler2" });
queries.push_back({ &locs_->samplerLocs_[0], "sampler0" });
queries.push_back({ &locs_->samplerLocs_[1], "sampler1" });
queries.push_back({ &locs_->samplerLocs_[2], "sampler2" });
samplersToCheck = 3;
}

_assert_(queries.size() <= MAX_TEXTURE_SLOTS);
for (size_t i = 0; i < dynamicUniforms.uniforms.size(); ++i) {
queries.push_back({ &dynamicUniformLocs_[i], dynamicUniforms.uniforms[i].name });
queries.push_back({ &locs_->dynamicUniformLocs_[i], dynamicUniforms.uniforms[i].name });
}
std::vector<GLRProgram::Initializer> initialize;
for (int i = 0; i < MAX_TEXTURE_SLOTS; ++i) {
if (i < (int)samplers_.size()) {
initialize.push_back({ &samplerLocs_[i], 0, i });
if (i < samplersToCheck) {
initialize.push_back({ &locs_->samplerLocs_[i], 0, i });
} else {
samplerLocs_[i] = -1;
locs_->samplerLocs_[i] = -1;
}
}

GLRProgramFlags flags{};
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, flags);
program_ = render_->CreateProgram(linkShaders, semantics, queries, initialize, locs_, flags);
return true;
}

Expand All @@ -1284,7 +1298,7 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {

for (size_t i = 0; i < curPipeline_->dynamicUniforms.uniforms.size(); ++i) {
const auto &uniform = curPipeline_->dynamicUniforms.uniforms[i];
const GLint &loc = curPipeline_->dynamicUniformLocs_[i];
const GLint &loc = curPipeline_->locs_->dynamicUniformLocs_[i];
const float *data = (const float *)((uint8_t *)ub + uniform.offset);
switch (uniform.type) {
case UniformType::FLOAT1:
Expand Down
22 changes: 22 additions & 0 deletions GPU/Common/PostShader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,25 @@ const TextureShaderInfo *GetTextureShaderInfo(const std::string &name) {
const std::vector<TextureShaderInfo> &GetAllTextureShaderInfo() {
return textureShaderInfo;
}

void FixPostShaderOrder(std::vector<std::string> *names) {
// There's one rule only that we enforce - only one shader can use UsePreviousFrame,
// and it has to be the last one. So we simply remove any we find from the list,
// and then append it to the end if there is one.
std::string prevFrameShader;
for (auto iter = names->begin(); iter != names->end(); ) {
const ShaderInfo *info = GetPostShaderInfo(*iter);
if (info) {
if (info->usePreviousFrame) {
prevFrameShader = *iter;
iter = names->erase(iter++);
continue;
}
}
++iter;
}

if (!prevFrameShader.empty()) {
names->push_back(prevFrameShader);
}
}
5 changes: 5 additions & 0 deletions GPU/Common/PostShader.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,8 @@ const std::vector<ShaderInfo> &GetAllPostShaderInfo();
const TextureShaderInfo *GetTextureShaderInfo(const std::string &name);
const std::vector<TextureShaderInfo> &GetAllTextureShaderInfo();
void RemoveUnknownPostShaders(std::vector<std::string> *names);

// Call this any time you alter the postshader list. It makes sure
// that "usePrevFrame" shaders are at the end, and that there's only one.
// It'll also enforce any similar future rules.
void FixPostShaderOrder(std::vector<std::string> *names);
1 change: 1 addition & 0 deletions GPU/Common/PresentationCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,7 @@ void PresentationCommon::CalculateRenderResolution(int *width, int *height, int
if (!g_Config.vPostShaderNames.empty()) {
ReloadAllPostShaderInfo(draw_);
RemoveUnknownPostShaders(&g_Config.vPostShaderNames);
FixPostShaderOrder(&g_Config.vPostShaderNames);
shaderInfo = GetFullPostShadersChain(g_Config.vPostShaderNames);
}

Expand Down
2 changes: 1 addition & 1 deletion GPU/GLES/ShaderManagerGLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs,
flags.useClipDistance0 = true;
}

program = render->CreateProgram(shaders, semantics, queries, initialize, flags);
program = render->CreateProgram(shaders, semantics, queries, initialize, nullptr, flags);

// The rest, use the "dirty" mechanism.
dirtyUniforms = DIRTY_ALL_UNIFORMS;
Expand Down
6 changes: 4 additions & 2 deletions GPU/Math3D.h
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,8 @@ inline void Vec3ByMatrix43(float vecOut[3], const float v[3], const float m[12])
vecOut[1] = vectorGetByIndex<1>(sum);
vecOut[2] = vectorGetByIndex<2>(sum);
#elif PPSSPP_ARCH(ARM64_NEON)
float32x4_t sum = Vec3ByMatrix43Internal(vld1q_f32(v), m);
float vecIn[4] = {v[0], v[1], v[2], 1.0f};
float32x4_t sum = Vec3ByMatrix43Internal(vld1q_f32(vecIn), m);
vecOut[0] = vgetq_lane_f32(sum, 0);
vecOut[1] = vgetq_lane_f32(sum, 1);
vecOut[2] = vgetq_lane_f32(sum, 2);
Expand Down Expand Up @@ -957,7 +958,8 @@ inline void Vec3ByMatrix44(float vecOut[4], const float v[3], const float m[16])
__m128 sum = Vec3ByMatrix44Internal(x, y, z, m);
_mm_storeu_ps(vecOut, sum);
#elif PPSSPP_ARCH(ARM64_NEON)
float32x4_t sum = Vec3ByMatrix44Internal(vld1q_f32(v), m);
float vecIn[4] = {v[0], v[1], v[2], 1.0f};
float32x4_t sum = Vec3ByMatrix44Internal(vld1q_f32(vecIn), m);
vst1q_f32(vecOut, sum);
#else
vecOut[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + m[12];
Expand Down
6 changes: 5 additions & 1 deletion UI/DisplayLayoutScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ void DisplayLayoutScreen::dialogFinished(const Screen *dialog, DialogResult resu
UI::EventReturn DisplayLayoutScreen::OnPostProcShaderChange(UI::EventParams &e) {
// Remove the virtual "Off" entry. TODO: Get rid of it generally.
g_Config.vPostShaderNames.erase(std::remove(g_Config.vPostShaderNames.begin(), g_Config.vPostShaderNames.end(), "Off"), g_Config.vPostShaderNames.end());
FixPostShaderOrder(&g_Config.vPostShaderNames);

NativeMessageReceived("gpu_configChanged", "");
NativeMessageReceived("gpu_renderResized", ""); // To deal with shaders that can change render resolution like upscaling.
Expand Down Expand Up @@ -370,7 +371,9 @@ void DisplayLayoutScreen::CreateViews() {
moreButton->OnClick.Add([=](EventParams &e) -> UI::EventReturn {
PopupContextMenuScreen *contextMenu = new UI::PopupContextMenuScreen(postShaderContextMenu, ARRAY_SIZE(postShaderContextMenu), di.get(), moreButton);
screenManager()->push(contextMenu);
contextMenu->SetEnabled(0, i > 0);
const ShaderInfo *info = GetPostShaderInfo(g_Config.vPostShaderNames[i]);
bool usesLastFrame = info ? info->usePreviousFrame : false;
contextMenu->SetEnabled(0, i > 0 && !usesLastFrame);
contextMenu->SetEnabled(1, i < g_Config.vPostShaderNames.size() - 1);
contextMenu->OnChoice.Add([=](EventParams &e) -> UI::EventReturn {
switch (e.a) {
Expand All @@ -386,6 +389,7 @@ void DisplayLayoutScreen::CreateViews() {
default:
return UI::EVENT_DONE;
}
FixPostShaderOrder(&g_Config.vPostShaderNames);
NativeMessageReceived("gpu_configChanged", "");
RecreateViews();
return UI::EVENT_DONE;
Expand Down
10 changes: 10 additions & 0 deletions assets/shaders/defaultshaders.ini
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,13 @@ Name=SideBySide Stereo
Author=Henrik Rydgård
Fragment=stereo_sbs.fsh
Vertex=fxaa.vsh
[LCDPersistence]
Name=LCD Persistence
Fragment=persistence.fsh
Vertex=fxaa.vsh
UsePreviousFrame=true
SettingName1=Strength
SettingDefaultValue1=0.4
SettingMaxValue1=0.8
SettingMinValue1=0.3
SettingStep1=0.05
14 changes: 14 additions & 0 deletions assets/shaders/persistence.fsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

uniform sampler2D sampler0;
uniform sampler2D sampler2;
uniform vec4 u_setting;
varying vec2 v_texcoord0;

void main() {
gl_FragColor.rgb = mix(texture2D(sampler0, v_texcoord0.xy).rgb, texture2D(sampler2, v_texcoord0.xy).rgb, u_setting.x);
gl_FragColor.a = 1.0;
}