mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
renderer_gl: Initial MSAA implementation
Basically copied over some of the paradigms over from Vulkan. Covers most rendering uses-cases except for conversions such as `ConvertDS24S8ToRGBA8` and `ConvertRGBA4ToRGB5A1`
This commit is contained in:
parent
29954c1392
commit
2743ebd0c9
7 changed files with 130 additions and 45 deletions
|
|
@ -19,10 +19,9 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent)
|
|||
SetConfiguration();
|
||||
|
||||
const auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
const bool res_scale_enabled = graphics_api != Settings::GraphicsAPI::Software;
|
||||
ui->resolution_factor_combobox->setEnabled(res_scale_enabled);
|
||||
const bool msaa_enabled = graphics_api == Settings::GraphicsAPI::Vulkan;
|
||||
ui->antialiasing_combobox->setEnabled(msaa_enabled);
|
||||
const bool hardware_graphics = graphics_api != Settings::GraphicsAPI::Software;
|
||||
ui->resolution_factor_combobox->setEnabled(hardware_graphics);
|
||||
ui->antialiasing_combobox->setEnabled(hardware_graphics);
|
||||
|
||||
connect(ui->render_3d_combobox, qOverload<int>(&QComboBox::currentIndexChanged), this,
|
||||
[this](int currentIndex) {
|
||||
|
|
|
|||
|
|
@ -30,13 +30,6 @@ u32 RendererBase::GetResolutionScaleFactor() {
|
|||
}
|
||||
|
||||
u8 RendererBase::GetSampleCount() const {
|
||||
const auto graphics_api = Settings::values.graphics_api.GetValue();
|
||||
|
||||
// Enabled for vulkan only for now
|
||||
if (graphics_api != Settings::GraphicsAPI::Vulkan) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Settings::GetAntiAliasingSampleCount(Settings::values.antialiasing.GetValue());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ BlitHelper::BlitHelper(const Driver& driver_)
|
|||
d24s8_to_rgba8{CreateProgram(HostShaders::D24S8_TO_RGBA8_FRAG, "D24S8_TO_RGBA8_FRAG")},
|
||||
rgba4_to_rgb5a1{CreateProgram(HostShaders::RGBA4_TO_RGB5A1_FRAG, "RGBA4_TO_RGB5A1_FRAG")} {
|
||||
vao.Create();
|
||||
read_fbo.Create();
|
||||
draw_fbo.Create();
|
||||
state.draw.vertex_array = vao.handle;
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
|
|
@ -156,6 +157,22 @@ bool BlitHelper::ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest,
|
|||
return true;
|
||||
}
|
||||
|
||||
void BlitHelper::ResolveTexture(Surface& surface) {
|
||||
|
||||
state.draw.read_framebuffer = read_fbo.handle;
|
||||
state.draw.draw_framebuffer = draw_fbo.handle;
|
||||
state.Apply();
|
||||
|
||||
surface.Attach(GL_READ_FRAMEBUFFER, 0, 0, 3);
|
||||
surface.Attach(GL_DRAW_FRAMEBUFFER, 0, 0, 1);
|
||||
const GLbitfield buffer_mask = surface.type == SurfaceType::Depth ? GL_DEPTH_BUFFER_BIT
|
||||
: surface.type == SurfaceType::DepthStencil
|
||||
? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
|
||||
: GL_COLOR_BUFFER_BIT;
|
||||
glBlitFramebuffer(0, 0, surface.GetScaledWidth(), surface.GetScaledHeight(), 0, 0,
|
||||
surface.GetScaledWidth(), surface.GetScaledHeight(), buffer_mask, GL_NEAREST);
|
||||
}
|
||||
|
||||
bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) {
|
||||
const auto filter = Settings::values.texture_filter.GetValue();
|
||||
const bool is_depth =
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2023 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -31,6 +31,8 @@ public:
|
|||
|
||||
bool ConvertRGBA4ToRGB5A1(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy);
|
||||
|
||||
void ResolveTexture(Surface& surface);
|
||||
|
||||
private:
|
||||
void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit);
|
||||
|
|
@ -47,6 +49,7 @@ private:
|
|||
const Driver& driver;
|
||||
OGLVertexArray vao;
|
||||
OpenGLState state;
|
||||
OGLFramebuffer read_fbo;
|
||||
OGLFramebuffer draw_fbo;
|
||||
OGLSampler linear_sampler;
|
||||
OGLSampler nearest_sampler;
|
||||
|
|
|
|||
|
|
@ -164,6 +164,10 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, Pica::PicaCore&
|
|||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.GetHandle());
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
glEnable(GL_SAMPLE_SHADING);
|
||||
glMinSampleShading(1.0f);
|
||||
}
|
||||
|
||||
RasterizerOpenGL::~RasterizerOpenGL() = default;
|
||||
|
|
|
|||
|
|
@ -92,13 +92,20 @@ static constexpr std::array<FormatTuple, 8> CUSTOM_TUPLES = {{
|
|||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] OGLTexture MakeHandle(GLenum target, u32 width, u32 height, u32 levels,
|
||||
[[nodiscard]] OGLTexture MakeHandle(GLenum target, u32 width, u32 height, u32 levels, u32 samples,
|
||||
const FormatTuple& tuple, std::string_view debug_name = "") {
|
||||
OGLTexture texture{};
|
||||
texture.Create();
|
||||
|
||||
glBindTexture(target, texture.handle);
|
||||
glTexStorage2D(target, levels, tuple.internal_format, width, height);
|
||||
if (samples > 1) {
|
||||
ASSERT(target == GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.handle);
|
||||
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, tuple.internal_format, width,
|
||||
height, false);
|
||||
} else {
|
||||
glBindTexture(target, texture.handle);
|
||||
glTexStorage2D(target, levels, tuple.internal_format, width, height);
|
||||
}
|
||||
|
||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
|
|
@ -215,6 +222,14 @@ bool TextureRuntime::ClearTextureWithoutFbo(Surface& surface,
|
|||
glClearTexSubImage(surface.Handle(), clear.texture_level, clear.texture_rect.left,
|
||||
clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(),
|
||||
clear.texture_rect.GetHeight(), 1, format, type, &clear.value);
|
||||
|
||||
if (surface.sample_count > 1) {
|
||||
// Clear MSAA too
|
||||
glClearTexSubImage(surface.Handle(3), clear.texture_level, clear.texture_rect.left,
|
||||
clear.texture_rect.bottom, 0, clear.texture_rect.GetWidth(),
|
||||
clear.texture_rect.GetHeight(), 1, format, type, &clear.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -279,6 +294,15 @@ bool TextureRuntime::CopyTextures(Surface& source, Surface& dest,
|
|||
|
||||
bool TextureRuntime::BlitTextures(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
// Must resolve images first
|
||||
// Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves
|
||||
if (source.sample_count > 1) {
|
||||
blit_helper.ResolveTexture(source);
|
||||
}
|
||||
if (dest.sample_count > 1) {
|
||||
blit_helper.ResolveTexture(dest);
|
||||
}
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
state.scissor.enabled = false;
|
||||
state.draw.read_framebuffer = read_fbos[FboIndex(source.type)].handle;
|
||||
|
|
@ -329,11 +353,16 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param
|
|||
const GLenum target =
|
||||
texture_type == VideoCore::TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
||||
|
||||
textures[0] = MakeHandle(target, width, height, levels, tuple, DebugName(false));
|
||||
textures[0] = MakeHandle(target, width, height, levels, 1, tuple, DebugName(false));
|
||||
if (res_scale != 1) {
|
||||
textures[1] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, tuple,
|
||||
textures[1] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, 1, tuple,
|
||||
DebugName(true, false));
|
||||
}
|
||||
|
||||
if (sample_count > 1) {
|
||||
textures[3] = MakeHandle(target, GetScaledWidth(), GetScaledHeight(), levels, sample_count,
|
||||
tuple, DebugName(true, false, sample_count));
|
||||
}
|
||||
}
|
||||
|
||||
Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface,
|
||||
|
|
@ -351,15 +380,19 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface
|
|||
custom_format = mat->format;
|
||||
material = mat;
|
||||
|
||||
textures[0] = MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(false));
|
||||
textures[0] = MakeHandle(target, mat->width, mat->height, levels, 1, tuple, DebugName(false));
|
||||
if (res_scale != 1) {
|
||||
textures[1] = MakeHandle(target, mat->width, mat->height, levels, DEFAULT_TUPLE,
|
||||
textures[1] = MakeHandle(target, mat->width, mat->height, levels, 1, DEFAULT_TUPLE,
|
||||
DebugName(true, true));
|
||||
}
|
||||
const bool has_normal = mat->Map(MapType::Normal);
|
||||
if (has_normal) {
|
||||
textures[2] =
|
||||
MakeHandle(target, mat->width, mat->height, levels, tuple, DebugName(true, true));
|
||||
MakeHandle(target, mat->width, mat->height, levels, 1, tuple, DebugName(true, true));
|
||||
}
|
||||
if (sample_count > 1) {
|
||||
textures[3] = MakeHandle(target, mat->width, mat->height, sample_count, levels,
|
||||
DEFAULT_TUPLE, DebugName(true, true, sample_count));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,8 +407,8 @@ GLuint Surface::Handle(u32 index) const noexcept {
|
|||
|
||||
GLuint Surface::CopyHandle() noexcept {
|
||||
if (!copy_texture.handle) {
|
||||
copy_texture = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple,
|
||||
DebugName(true));
|
||||
copy_texture = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, 1,
|
||||
tuple, DebugName(true));
|
||||
}
|
||||
|
||||
for (u32 level = 0; level < levels; level++) {
|
||||
|
|
@ -534,22 +567,26 @@ bool Surface::DownloadWithoutFbo(const VideoCore::BufferTextureCopy& download,
|
|||
return false;
|
||||
}
|
||||
|
||||
void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) {
|
||||
const GLuint handle = Handle(static_cast<u32>(scaled));
|
||||
const GLenum textarget = texture_type == TextureType::CubeMap
|
||||
? GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer
|
||||
: GL_TEXTURE_2D;
|
||||
void Surface::Attach(GLenum target, u32 level, u32 layer, u32 handle) {
|
||||
const GLuint gl_handle = Handle(handle);
|
||||
GLenum textarget = texture_type == TextureType::CubeMap ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer
|
||||
: GL_TEXTURE_2D;
|
||||
|
||||
if (handle == 3 && sample_count > 1) {
|
||||
ASSERT(texture_type == TextureType::Texture2D);
|
||||
textarget = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SurfaceType::Color:
|
||||
case SurfaceType::Texture:
|
||||
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, handle, level);
|
||||
glFramebufferTexture2D(target, GL_COLOR_ATTACHMENT0, textarget, gl_handle, level);
|
||||
break;
|
||||
case SurfaceType::Depth:
|
||||
glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, handle, level);
|
||||
glFramebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, gl_handle, level);
|
||||
break;
|
||||
case SurfaceType::DepthStencil:
|
||||
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, handle, level);
|
||||
glFramebufferTexture2D(target, GL_DEPTH_STENCIL_ATTACHMENT, textarget, gl_handle, level);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid surface type!");
|
||||
|
|
@ -557,17 +594,11 @@ void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) {
|
|||
}
|
||||
|
||||
void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) {
|
||||
if (res_scale == new_scale && sample_count == new_sample_count) {
|
||||
return;
|
||||
}
|
||||
const bool res_scale_modified = res_scale != new_scale;
|
||||
if (res_scale_modified && new_scale > 1) {
|
||||
|
||||
res_scale = new_scale;
|
||||
sample_count = new_sample_count;
|
||||
|
||||
if (res_scale > 1) {
|
||||
|
||||
textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple,
|
||||
DebugName(true));
|
||||
textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, 1,
|
||||
tuple, DebugName(true));
|
||||
for (u32 level = 0; level < levels; level++) {
|
||||
const VideoCore::TextureBlit blit = {
|
||||
.src_level = level,
|
||||
|
|
@ -579,8 +610,10 @@ void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) {
|
|||
}
|
||||
}
|
||||
|
||||
if (new_sample_count > 1) {
|
||||
if ((res_scale_modified || sample_count != new_sample_count) && new_sample_count > 1) {
|
||||
// Todo(wunk): OpenGL MSAA
|
||||
textures[3] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels,
|
||||
sample_count, tuple, DebugName(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +647,8 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) {
|
|||
Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params,
|
||||
const Surface* color, const Surface* depth)
|
||||
: VideoCore::FramebufferParams{params},
|
||||
res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)} {
|
||||
res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)},
|
||||
sample_count{color ? color->sample_count : (depth ? depth->sample_count : 1u)} {
|
||||
|
||||
if (shadow_rendering && !color) {
|
||||
return;
|
||||
|
|
@ -627,6 +661,15 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
|
|||
attachments[1] = depth->Handle();
|
||||
}
|
||||
|
||||
if (sample_count > 1) {
|
||||
if (color) {
|
||||
attachments[2] = color->Handle(3);
|
||||
}
|
||||
if (depth) {
|
||||
attachments[3] = depth->Handle(3);
|
||||
}
|
||||
}
|
||||
|
||||
framebuffer.Create();
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
|
|
@ -658,6 +701,27 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa
|
|||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
if (sample_count > 1) {
|
||||
if (color) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D_MULTISAMPLE, color ? color->Handle(3) : 0,
|
||||
color_level);
|
||||
}
|
||||
if (depth) {
|
||||
if (depth->pixel_format == PixelFormat::D24S8) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D_MULTISAMPLE, depth->Handle(3),
|
||||
depth_level);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_TEXTURE_2D_MULTISAMPLE, depth->Handle(3),
|
||||
depth_level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D_MULTISAMPLE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ public:
|
|||
const VideoCore::StagingData& staging);
|
||||
|
||||
/// Attaches a handle of surface to the specified framebuffer target
|
||||
void Attach(GLenum target, u32 level, u32 layer, bool scaled = true);
|
||||
void Attach(GLenum target, u32 level, u32 layer, u32 handle = 1);
|
||||
|
||||
/// Scales up the surface to match the new resolution scale and sample-count.
|
||||
void ScaleUp(u32 new_scale, u8 new_sample_count);
|
||||
|
|
@ -149,7 +149,7 @@ private:
|
|||
private:
|
||||
const Driver* driver;
|
||||
TextureRuntime* runtime;
|
||||
std::array<OGLTexture, 3> textures;
|
||||
std::array<OGLTexture, 4> textures;
|
||||
OGLTexture copy_texture;
|
||||
FormatTuple tuple;
|
||||
};
|
||||
|
|
@ -170,6 +170,10 @@ public:
|
|||
return res_scale;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 Samples() const noexcept {
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
[[nodiscard]] GLuint Handle() const noexcept {
|
||||
return framebuffer.handle;
|
||||
}
|
||||
|
|
@ -184,7 +188,8 @@ public:
|
|||
|
||||
private:
|
||||
u32 res_scale{1};
|
||||
std::array<GLuint, 2> attachments{};
|
||||
u32 sample_count{1};
|
||||
std::array<GLuint, 4> attachments{};
|
||||
OGLFramebuffer framebuffer;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue