rasterizer_cache: Factor morton swizzle and pixel format to dedicate headers

* Makes the code cleaner in general by not having to alias PixelFormat and SurfaceType everywhere
master
emufan4568 2022-08-20 12:17:31 +07:00
parent efc2db4088
commit 6a7d601e42
13 changed files with 427 additions and 409 deletions

@ -23,6 +23,8 @@ add_library(video_core STATIC
regs_texturing.h
renderer_base.cpp
renderer_base.h
rasterizer_cache/morton_swizzle.h
rasterizer_cache/pixel_format.h
rasterizer_cache/rasterizer_cache.cpp
rasterizer_cache/rasterizer_cache.h
rasterizer_cache/surface_params.cpp

@ -0,0 +1,171 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/alignment.h"
#include "core/memory.h"
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/renderer_opengl/gl_vars.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
namespace OpenGL {
template <bool morton_to_gl, PixelFormat format>
static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
constexpr u32 aligned_bytes_per_pixel = GetBytesPerPixel(format);
for (u32 y = 0; y < 8; ++y) {
for (u32 x = 0; x < 8; ++x) {
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * aligned_bytes_per_pixel;
if constexpr (morton_to_gl) {
if constexpr (format == PixelFormat::D24S8) {
gl_ptr[0] = tile_ptr[3];
std::memcpy(gl_ptr + 1, tile_ptr, 3);
} else if (format == PixelFormat::RGBA8 && GLES) {
// because GLES does not have ABGR format
// so we will do byteswapping here
gl_ptr[0] = tile_ptr[3];
gl_ptr[1] = tile_ptr[2];
gl_ptr[2] = tile_ptr[1];
gl_ptr[3] = tile_ptr[0];
} else if (format == PixelFormat::RGB8 && GLES) {
gl_ptr[0] = tile_ptr[2];
gl_ptr[1] = tile_ptr[1];
gl_ptr[2] = tile_ptr[0];
} else {
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
}
} else {
if constexpr (format == PixelFormat::D24S8) {
std::memcpy(tile_ptr, gl_ptr + 1, 3);
tile_ptr[3] = gl_ptr[0];
} else if (format == PixelFormat::RGBA8 && GLES) {
// because GLES does not have ABGR format
// so we will do byteswapping here
tile_ptr[0] = gl_ptr[3];
tile_ptr[1] = gl_ptr[2];
tile_ptr[2] = gl_ptr[1];
tile_ptr[3] = gl_ptr[0];
} else if (format == PixelFormat::RGB8 && GLES) {
tile_ptr[0] = gl_ptr[2];
tile_ptr[1] = gl_ptr[1];
tile_ptr[2] = gl_ptr[0];
} else {
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
}
}
}
}
}
template <bool morton_to_gl, PixelFormat format>
static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, PAddr base, PAddr start, PAddr end) {
constexpr u32 bytes_per_pixel = GetFormatBpp(format) / 8;
constexpr u32 tile_size = bytes_per_pixel * 64;
constexpr u32 aligned_bytes_per_pixel = GetBytesPerPixel(format);
static_assert(aligned_bytes_per_pixel >= bytes_per_pixel, "");
gl_buffer += aligned_bytes_per_pixel - bytes_per_pixel;
const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size);
const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size);
const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size);
ASSERT(!morton_to_gl || (aligned_start == start && aligned_end == end));
const u32 begin_pixel_index = (aligned_down_start - base) / bytes_per_pixel;
u32 x = (begin_pixel_index % (stride * 8)) / 8;
u32 y = (begin_pixel_index / (stride * 8)) * 8;
gl_buffer += ((height - 8 - y) * stride + x) * aligned_bytes_per_pixel;
auto glbuf_next_tile = [&] {
x = (x + 8) % stride;
gl_buffer += 8 * aligned_bytes_per_pixel;
if (!x) {
y += 8;
gl_buffer -= stride * 9 * aligned_bytes_per_pixel;
}
};
u8* tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start);
if (start < aligned_start && !morton_to_gl) {
std::array<u8, tile_size> tmp_buf;
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start],
std::min(aligned_start, end) - start);
tile_buffer += aligned_start - start;
glbuf_next_tile();
}
const u8* const buffer_end = tile_buffer + aligned_end - aligned_start;
PAddr current_paddr = aligned_start;
while (tile_buffer < buffer_end) {
// Pokemon Super Mystery Dungeon will try to use textures that go beyond
// the end address of VRAM. Stop reading if reaches invalid address
if (!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr) ||
!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr + tile_size)) {
LOG_ERROR(Render_OpenGL, "Out of bound texture");
break;
}
MortonCopyTile<morton_to_gl, format>(stride, tile_buffer, gl_buffer);
tile_buffer += tile_size;
current_paddr += tile_size;
glbuf_next_tile();
}
if (end > std::max(aligned_start, aligned_end) && !morton_to_gl) {
std::array<u8, tile_size> tmp_buf;
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end);
}
}
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> morton_to_gl_fns = {
MortonCopy<true, PixelFormat::RGBA8>, // 0
MortonCopy<true, PixelFormat::RGB8>, // 1
MortonCopy<true, PixelFormat::RGB5A1>, // 2
MortonCopy<true, PixelFormat::RGB565>, // 3
MortonCopy<true, PixelFormat::RGBA4>, // 4
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, // 5 - 13
MortonCopy<true, PixelFormat::D16>, // 14
nullptr, // 15
MortonCopy<true, PixelFormat::D24>, // 16
MortonCopy<true, PixelFormat::D24S8> // 17
};
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl_to_morton_fns = {
MortonCopy<false, PixelFormat::RGBA8>, // 0
MortonCopy<false, PixelFormat::RGB8>, // 1
MortonCopy<false, PixelFormat::RGB5A1>, // 2
MortonCopy<false, PixelFormat::RGB565>, // 3
MortonCopy<false, PixelFormat::RGBA4>, // 4
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, // 5 - 13
MortonCopy<false, PixelFormat::D16>, // 14
nullptr, // 15
MortonCopy<false, PixelFormat::D24>, // 16
MortonCopy<false, PixelFormat::D24S8> // 17
};
} // namespace OpenGL

@ -0,0 +1,194 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string_view>
#include "core/hw/gpu.h"
#include "video_core/regs_texturing.h"
#include "video_core/regs_framebuffer.h"
namespace OpenGL {
enum class PixelFormat : u8 {
// First 5 formats are shared between textures and color buffers
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
// Texture-only formats
IA8 = 5,
RG8 = 6,
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12,
ETC1A4 = 13,
// Depth buffer-only formats
D16 = 14,
D24 = 16,
D24S8 = 17,
Invalid = 255,
};
enum class SurfaceType {
Color = 0,
Texture = 1,
Depth = 2,
DepthStencil = 3,
Fill = 4,
Invalid = 5
};
static constexpr std::string_view PixelFormatAsString(PixelFormat format) {
switch (format) {
case PixelFormat::RGBA8:
return "RGBA8";
case PixelFormat::RGB8:
return "RGB8";
case PixelFormat::RGB5A1:
return "RGB5A1";
case PixelFormat::RGB565:
return "RGB565";
case PixelFormat::RGBA4:
return "RGBA4";
case PixelFormat::IA8:
return "IA8";
case PixelFormat::RG8:
return "RG8";
case PixelFormat::I8:
return "I8";
case PixelFormat::A8:
return "A8";
case PixelFormat::IA4:
return "IA4";
case PixelFormat::I4:
return "I4";
case PixelFormat::A4:
return "A4";
case PixelFormat::ETC1:
return "ETC1";
case PixelFormat::ETC1A4:
return "ETC1A4";
case PixelFormat::D16:
return "D16";
case PixelFormat::D24:
return "D24";
case PixelFormat::D24S8:
return "D24S8";
default:
return "NotReal";
}
}
static constexpr PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
const u32 format_index = static_cast<u32>(format);
return (format_index < 14) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
}
static constexpr PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
const u32 format_index = static_cast<u32>(format);
return (format_index < 5) ? static_cast<PixelFormat>(format) : PixelFormat::Invalid;
}
static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
const u32 format_index = static_cast<u32>(format);
return (format_index < 4) ? static_cast<PixelFormat>(format_index + 14)
: PixelFormat::Invalid;
}
static constexpr PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
switch (format) {
// RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
case GPU::Regs::PixelFormat::RGB565:
return PixelFormat::RGB565;
case GPU::Regs::PixelFormat::RGB5A1:
return PixelFormat::RGB5A1;
default:
return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid;
}
}
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
const u32 format_index = static_cast<u32>(pixel_format);
if (format_index < 5) {
return SurfaceType::Color;
}
if (format_index < 14) {
return SurfaceType::Texture;
}
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
return SurfaceType::Depth;
}
if (pixel_format == PixelFormat::D24S8) {
return SurfaceType::DepthStencil;
}
return SurfaceType::Invalid;
}
static constexpr bool CheckFormatsBlittable(PixelFormat source_format, PixelFormat dest_format) {
SurfaceType source_type = GetFormatType(source_format);
SurfaceType dest_type = GetFormatType(dest_format);
if ((source_type == SurfaceType::Color || source_type == SurfaceType::Texture) &&
(dest_type == SurfaceType::Color || dest_type == SurfaceType::Texture)) {
return true;
}
if (source_type == SurfaceType::Depth && dest_type == SurfaceType::Depth) {
return true;
}
if (source_type == SurfaceType::DepthStencil && dest_type == SurfaceType::DepthStencil) {
return true;
}
return false;
}
static constexpr u32 GetFormatBpp(PixelFormat format) {
switch (format) {
case PixelFormat::RGBA8:
case PixelFormat::D24S8:
return 32;
case PixelFormat::RGB8:
case PixelFormat::D24:
return 24;
case PixelFormat::RGB5A1:
case PixelFormat::RGB565:
case PixelFormat::RGBA4:
case PixelFormat::IA8:
case PixelFormat::RG8:
case PixelFormat::D16:
return 16;
case PixelFormat::I8:
case PixelFormat::A8:
case PixelFormat::IA4:
case PixelFormat::ETC1A4:
return 8;
case PixelFormat::I4:
case PixelFormat::A4:
case PixelFormat::ETC1:
return 4;
case PixelFormat::Invalid:
return 0;
}
}
static constexpr u32 GetBytesPerPixel(PixelFormat format) {
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
if (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) {
return 4;
}
return GetFormatBpp(format) / 8;
}
} // namespace OpenGL

@ -29,24 +29,17 @@
#include "core/custom_tex_cache.h"
#include "core/frontend/emu_window.h"
#include "core/hle/kernel/process.h"
#include "core/memory.h"
#include "core/settings.h"
#include "video_core/pica_state.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
#include "video_core/rasterizer_cache/morton_swizzle.h"
#include "video_core/rasterizer_cache/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_vars.h"
#include "video_core/renderer_opengl/texture_downloader_es.h"
#include "video_core/renderer_opengl/texture_filters/texture_filterer.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
namespace OpenGL {
using SurfaceType = SurfaceParams::SurfaceType;
using PixelFormat = SurfaceParams::PixelFormat;
static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
{GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
@ -67,7 +60,7 @@ static constexpr std::array<FormatTuple, 5> fb_format_tuples_oes = {{
}};
const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
const SurfaceType type = GetFormatType(pixel_format);
if (type == SurfaceType::Color) {
ASSERT(static_cast<std::size_t>(pixel_format) < fb_format_tuples.size());
if (GLES) {
@ -87,162 +80,6 @@ static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
}
template <bool morton_to_gl, PixelFormat format>
static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
for (u32 y = 0; y < 8; ++y) {
for (u32 x = 0; x < 8; ++x) {
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
if constexpr (morton_to_gl) {
if constexpr (format == PixelFormat::D24S8) {
gl_ptr[0] = tile_ptr[3];
std::memcpy(gl_ptr + 1, tile_ptr, 3);
} else if (format == PixelFormat::RGBA8 && GLES) {
// because GLES does not have ABGR format
// so we will do byteswapping here
gl_ptr[0] = tile_ptr[3];
gl_ptr[1] = tile_ptr[2];
gl_ptr[2] = tile_ptr[1];
gl_ptr[3] = tile_ptr[0];
} else if (format == PixelFormat::RGB8 && GLES) {
gl_ptr[0] = tile_ptr[2];
gl_ptr[1] = tile_ptr[1];
gl_ptr[2] = tile_ptr[0];
} else {
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
}
} else {
if constexpr (format == PixelFormat::D24S8) {
std::memcpy(tile_ptr, gl_ptr + 1, 3);
tile_ptr[3] = gl_ptr[0];
} else if (format == PixelFormat::RGBA8 && GLES) {
// because GLES does not have ABGR format
// so we will do byteswapping here
tile_ptr[0] = gl_ptr[3];
tile_ptr[1] = gl_ptr[2];
tile_ptr[2] = gl_ptr[1];
tile_ptr[3] = gl_ptr[0];
} else if (format == PixelFormat::RGB8 && GLES) {
tile_ptr[0] = gl_ptr[2];
tile_ptr[1] = gl_ptr[1];
tile_ptr[2] = gl_ptr[0];
} else {
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
}
}
}
}
}
template <bool morton_to_gl, PixelFormat format>
static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, PAddr base, PAddr start, PAddr end) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
constexpr u32 tile_size = bytes_per_pixel * 64;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
static_assert(gl_bytes_per_pixel >= bytes_per_pixel, "");
gl_buffer += gl_bytes_per_pixel - bytes_per_pixel;
const PAddr aligned_down_start = base + Common::AlignDown(start - base, tile_size);
const PAddr aligned_start = base + Common::AlignUp(start - base, tile_size);
const PAddr aligned_end = base + Common::AlignDown(end - base, tile_size);
ASSERT(!morton_to_gl || (aligned_start == start && aligned_end == end));
const u32 begin_pixel_index = (aligned_down_start - base) / bytes_per_pixel;
u32 x = (begin_pixel_index % (stride * 8)) / 8;
u32 y = (begin_pixel_index / (stride * 8)) * 8;
gl_buffer += ((height - 8 - y) * stride + x) * gl_bytes_per_pixel;
auto glbuf_next_tile = [&] {
x = (x + 8) % stride;
gl_buffer += 8 * gl_bytes_per_pixel;
if (!x) {
y += 8;
gl_buffer -= stride * 9 * gl_bytes_per_pixel;
}
};
u8* tile_buffer = VideoCore::g_memory->GetPhysicalPointer(start);
if (start < aligned_start && !morton_to_gl) {
std::array<u8, tile_size> tmp_buf;
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
std::memcpy(tile_buffer, &tmp_buf[start - aligned_down_start],
std::min(aligned_start, end) - start);
tile_buffer += aligned_start - start;
glbuf_next_tile();
}
const u8* const buffer_end = tile_buffer + aligned_end - aligned_start;
PAddr current_paddr = aligned_start;
while (tile_buffer < buffer_end) {
// Pokemon Super Mystery Dungeon will try to use textures that go beyond
// the end address of VRAM. Stop reading if reaches invalid address
if (!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr) ||
!VideoCore::g_memory->IsValidPhysicalAddress(current_paddr + tile_size)) {
LOG_ERROR(Render_OpenGL, "Out of bound texture");
break;
}
MortonCopyTile<morton_to_gl, format>(stride, tile_buffer, gl_buffer);
tile_buffer += tile_size;
current_paddr += tile_size;
glbuf_next_tile();
}
if (end > std::max(aligned_start, aligned_end) && !morton_to_gl) {
std::array<u8, tile_size> tmp_buf;
MortonCopyTile<morton_to_gl, format>(stride, &tmp_buf[0], gl_buffer);
std::memcpy(tile_buffer, &tmp_buf[0], end - aligned_end);
}
}
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> morton_to_gl_fns = {
MortonCopy<true, PixelFormat::RGBA8>, // 0
MortonCopy<true, PixelFormat::RGB8>, // 1
MortonCopy<true, PixelFormat::RGB5A1>, // 2
MortonCopy<true, PixelFormat::RGB565>, // 3
MortonCopy<true, PixelFormat::RGBA4>, // 4
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, // 5 - 13
MortonCopy<true, PixelFormat::D16>, // 14
nullptr, // 15
MortonCopy<true, PixelFormat::D24>, // 16
MortonCopy<true, PixelFormat::D24S8> // 17
};
static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gl_to_morton_fns = {
MortonCopy<false, PixelFormat::RGBA8>, // 0
MortonCopy<false, PixelFormat::RGB8>, // 1
MortonCopy<false, PixelFormat::RGB5A1>, // 2
MortonCopy<false, PixelFormat::RGB565>, // 3
MortonCopy<false, PixelFormat::RGBA4>, // 4
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, // 5 - 13
MortonCopy<false, PixelFormat::D16>, // 14
nullptr, // 15
MortonCopy<false, PixelFormat::D24>, // 16
MortonCopy<false, PixelFormat::D24S8> // 17
};
// Allocate an uninitialized texture of appropriate size and format for the surface
OGLTexture RasterizerCacheOpenGL::AllocateSurfaceTexture(const FormatTuple& format_tuple, u32 width,
u32 height) {
@ -264,7 +101,7 @@ OGLTexture RasterizerCacheOpenGL::AllocateSurfaceTexture(const FormatTuple& form
if (GL_ARB_texture_storage) {
// Allocate all possible mipmap levels upfront
auto levels = std::log2(std::max(width, height)) + 1;
const GLsizei levels = std::log2(std::max(width, height)) + 1;
glTexStorage2D(GL_TEXTURE_2D, levels, format_tuple.internal_format, width, height);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
@ -293,7 +130,7 @@ static void AllocateTextureCube(GLuint texture, const FormatTuple& format_tuple,
glActiveTexture(TextureUnits::TextureCube.Enum());
if (GL_ARB_texture_storage) {
// Allocate all possible mipmap levels in case the game uses them later
auto levels = std::log2(width) + 1;
const GLsizei levels = std::log2(width) + 1;
glTexStorage2D(GL_TEXTURE_CUBE_MAP, levels, format_tuple.internal_format, width, width);
} else {
for (auto faces : {
@ -418,10 +255,10 @@ static bool FillSurface(const Surface& surface, const u8* fill_data,
u32 value_32bit = 0;
GLfloat value_float;
if (surface->pixel_format == SurfaceParams::PixelFormat::D16) {
if (surface->pixel_format == PixelFormat::D16) {
std::memcpy(&value_32bit, fill_data, 2);
value_float = value_32bit / 65535.0f; // 2^16 - 1
} else if (surface->pixel_format == SurfaceParams::PixelFormat::D24) {
} else if (surface->pixel_format == PixelFormat::D24) {
std::memcpy(&value_32bit, fill_data, 3);
value_float = value_32bit / 16777215.0f; // 2^24 - 1
}
@ -545,7 +382,7 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) {
return;
if (gl_buffer.empty()) {
gl_buffer.resize(width * height * GetGLBytesPerPixel(pixel_format));
gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
}
// TODO: Should probably be done in ::Memory:: and check for other regions too
@ -617,7 +454,7 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) {
if (dst_buffer == nullptr)
return;
ASSERT(gl_buffer.size() == width * height * GetGLBytesPerPixel(pixel_format));
ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
// TODO: Should probably be done in ::Memory:: and check for other regions too
// same as loadglbuffer()
@ -771,7 +608,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
MICROPROFILE_SCOPE(OpenGL_TextureUL);
ASSERT(gl_buffer.size() == width * height * GetGLBytesPerPixel(pixel_format));
ASSERT(gl_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
u64 tex_hash = 0;
@ -786,7 +623,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
// Load data from memory to the surface
GLint x0 = static_cast<GLint>(rect.left);
GLint y0 = static_cast<GLint>(rect.bottom);
std::size_t buffer_offset = (y0 * stride + x0) * GetGLBytesPerPixel(pixel_format);
std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(pixel_format);
const FormatTuple& tuple = GetFormatTuple(pixel_format);
GLuint target_tex = texture.handle;
@ -814,7 +651,7 @@ void CachedSurface::UploadGLTexture(Common::Rectangle<u32> rect, GLuint read_fb_
cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
if (is_custom) {
if (res_scale == 1) {
texture = owner.AllocateSurfaceTexture(GetFormatTuple(PixelFormat::RGBA8),
@ -873,7 +710,7 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint
MICROPROFILE_SCOPE(OpenGL_TextureDL);
if (gl_buffer.empty()) {
gl_buffer.resize(width * height * GetGLBytesPerPixel(pixel_format));
gl_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
}
OpenGLState state = OpenGLState::GetCurState();
@ -883,10 +720,10 @@ void CachedSurface::DownloadGLTexture(const Common::Rectangle<u32>& rect, GLuint
const FormatTuple& tuple = GetFormatTuple(pixel_format);
// Ensure no bad interactions with GL_PACK_ALIGNMENT
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
std::size_t buffer_offset =
(rect.bottom * stride + rect.left) * GetGLBytesPerPixel(pixel_format);
(rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
if (res_scale != 1) {
@ -1084,7 +921,7 @@ bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface,
const Common::Rectangle<u32>& dst_rect) {
MICROPROFILE_SCOPE(OpenGL_BlitSurface);
if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format))
if (!CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format))
return false;
dst_surface->InvalidateAllWatcher();
@ -1239,7 +1076,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf
params.width = info.width;
params.height = info.height;
params.is_tiled = true;
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
params.pixel_format = PixelFormatFromTextureFormat(info.format);
params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor;
params.UpdateParams();
@ -1411,7 +1248,7 @@ const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCube
cube.texture.Create();
AllocateTextureCube(
cube.texture.handle,
GetFormatTuple(CachedSurface::PixelFormatFromTextureFormat(config.format)),
GetFormatTuple(PixelFormatFromTextureFormat(config.format)),
cube.res_scale * config.width);
}
@ -1459,7 +1296,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
const auto& config = regs.framebuffer.framebuffer;
// update resolution_scale_factor and reset cache if changed
if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) |
if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) ||
(VideoCore::g_texture_filter_update_requested.exchange(false) &&
texture_filterer->Reset(Settings::values.texture_filter_name, resolution_scale_factor))) {
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
@ -1485,11 +1322,11 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
SurfaceParams depth_params = color_params;
color_params.addr = config.GetColorBufferPhysicalAddress();
color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format);
color_params.pixel_format = PixelFormatFromColorFormat(config.color_format);
color_params.UpdateParams();
depth_params.addr = config.GetDepthBufferPhysicalAddress();
depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format);
depth_params.pixel_format = PixelFormatFromDepthFormat(config.depth_format);
depth_params.UpdateParams();
auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped);
@ -1693,7 +1530,7 @@ bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surf
};
bool implemented = true;
for (PixelFormat format : all_formats) {
if (SurfaceParams::GetFormatBpp(format) == surface->GetFormatBpp()) {
if (GetFormatBpp(format) == surface->GetFormatBpp()) {
params.pixel_format = format;
// This could potentially be expensive,
// although experimentally it hasn't been too bad
@ -1701,8 +1538,8 @@ bool RasterizerCacheOpenGL::NoUnimplementedReinterpretations(const Surface& surf
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
if (test_surface != nullptr) {
LOG_WARNING(Render_OpenGL, "Missing pixel_format reinterpreter: {} -> {}",
SurfaceParams::PixelFormatAsString(format),
SurfaceParams::PixelFormatAsString(surface->pixel_format));
PixelFormatAsString(format),
PixelFormatAsString(surface->pixel_format));
implemented = false;
}
}
@ -1751,9 +1588,8 @@ bool RasterizerCacheOpenGL::ValidateByReinterpretation(const Surface& surface,
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
read_framebuffer.handle, tmp_tex.handle,
tmp_rect, draw_framebuffer.handle);
SurfaceParams::SurfaceType type =
SurfaceParams::GetFormatType(reinterpreter->first.dst_format);
const SurfaceType type = GetFormatType(reinterpreter->first.dst_format);
if (!texture_filterer->Filter(tmp_tex.handle, tmp_rect, surface->texture.handle,
dest_rect, type, read_framebuffer.handle,
draw_framebuffer.handle)) {

@ -45,7 +45,7 @@ struct FormatTuple {
constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
const FormatTuple& GetFormatTuple(SurfaceParams::PixelFormat pixel_format);
const FormatTuple& GetFormatTuple(PixelFormat pixel_format);
struct HostTextureTag {
FormatTuple format_tuple;
@ -205,15 +205,6 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
bool is_custom = false;
Core::CustomTexInfo custom_tex_info;
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
return format == PixelFormat::Invalid
? 0
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
? 4
: SurfaceParams::GetFormatBpp(format) / 8;
}
std::vector<u8> gl_buffer;
// Read/Write data in 3DS memory to/from gl_buffer

@ -12,10 +12,11 @@ SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
SurfaceParams params = *this;
const u32 tiled_size = is_tiled ? 8 : 1;
const u32 stride_tiled_bytes = BytesInPixels(stride * tiled_size);
PAddr aligned_start =
addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes);
addr + Common::AlignDown(boost::icl::first(interval) - addr, stride_tiled_bytes);
PAddr aligned_end =
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes);
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, stride_tiled_bytes);
if (aligned_end - aligned_start > stride_tiled_bytes) {
params.addr = aligned_start;
@ -24,17 +25,19 @@ SurfaceParams SurfaceParams::FromInterval(SurfaceInterval interval) const {
// 1 row
ASSERT(aligned_end - aligned_start == stride_tiled_bytes);
const u32 tiled_alignment = BytesInPixels(is_tiled ? 8 * 8 : 1);
aligned_start =
addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
addr + Common::AlignDown(boost::icl::first(interval) - addr, tiled_alignment);
aligned_end =
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
addr + Common::AlignUp(boost::icl::last_next(interval) - addr, tiled_alignment);
params.addr = aligned_start;
params.width = PixelsInBytes(aligned_end - aligned_start) / tiled_size;
params.stride = params.width;
params.height = tiled_size;
}
params.UpdateParams();
params.UpdateParams();
return params;
}
@ -158,6 +161,7 @@ bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
end < texcopy_params.end) {
return false;
}
if (texcopy_params.width != texcopy_params.stride) {
const u32 tile_stride = BytesInPixels(stride * (is_tiled ? 8 : 1));
return (texcopy_params.addr - addr) % BytesInPixels(is_tiled ? 64 : 1) == 0 &&
@ -165,6 +169,7 @@ bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const {
(texcopy_params.height == 1 || texcopy_params.stride == tile_stride) &&
((texcopy_params.addr - addr) % tile_stride) + texcopy_params.width <= tile_stride;
}
return FromInterval(texcopy_params.GetInterval()).GetInterval() == texcopy_params.GetInterval();
}

@ -7,11 +7,8 @@
#include <array>
#include <climits>
#include <boost/icl/interval.hpp>
#include "common/assert.h"
#include "common/math_util.h"
#include "core/hw/gpu.h"
#include "video_core/regs_framebuffer.h"
#include "video_core/regs_texturing.h"
#include "video_core/rasterizer_cache/pixel_format.h"
namespace OpenGL {
@ -21,180 +18,8 @@ using Surface = std::shared_ptr<CachedSurface>;
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
struct SurfaceParams {
private:
static constexpr std::array<unsigned int, 18> BPP_TABLE = {
32, // RGBA8
24, // RGB8
16, // RGB5A1
16, // RGB565
16, // RGBA4
16, // IA8
16, // RG8
8, // I8
8, // A8
8, // IA4
4, // I4
4, // A4
4, // ETC1
8, // ETC1A4
16, // D16
0,
24, // D24
32, // D24S8
};
public:
enum class PixelFormat {
// First 5 formats are shared between textures and color buffers
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
// Texture-only formats
IA8 = 5,
RG8 = 6,
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12,
ETC1A4 = 13,
// Depth buffer-only formats
D16 = 14,
// gap
D24 = 16,
D24S8 = 17,
Invalid = 255,
};
enum class SurfaceType {
Color = 0,
Texture = 1,
Depth = 2,
DepthStencil = 3,
Fill = 4,
Invalid = 5
};
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
const auto format_idx = static_cast<std::size_t>(format);
DEBUG_ASSERT_MSG(format_idx < BPP_TABLE.size(), "Invalid pixel format {}", format_idx);
return BPP_TABLE[format_idx];
}
unsigned int GetFormatBpp() const {
return GetFormatBpp(pixel_format);
}
static std::string_view PixelFormatAsString(PixelFormat format) {
switch (format) {
case PixelFormat::RGBA8:
return "RGBA8";
case PixelFormat::RGB8:
return "RGB8";
case PixelFormat::RGB5A1:
return "RGB5A1";
case PixelFormat::RGB565:
return "RGB565";
case PixelFormat::RGBA4:
return "RGBA4";
case PixelFormat::IA8:
return "IA8";
case PixelFormat::RG8:
return "RG8";
case PixelFormat::I8:
return "I8";
case PixelFormat::A8:
return "A8";
case PixelFormat::IA4:
return "IA4";
case PixelFormat::I4:
return "I4";
case PixelFormat::A4:
return "A4";
case PixelFormat::ETC1:
return "ETC1";
case PixelFormat::ETC1A4:
return "ETC1A4";
case PixelFormat::D16:
return "D16";
case PixelFormat::D24:
return "D24";
case PixelFormat::D24S8:
return "D24S8";
default:
return "Not a real pixel format";
}
}
static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid;
}
static PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid;
}
static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14)
: PixelFormat::Invalid;
}
static PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) {
switch (format) {
// RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat
case GPU::Regs::PixelFormat::RGB565:
return PixelFormat::RGB565;
case GPU::Regs::PixelFormat::RGB5A1:
return PixelFormat::RGB5A1;
default:
return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid;
}
}
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
SurfaceType a_type = GetFormatType(pixel_format_a);
SurfaceType b_type = GetFormatType(pixel_format_b);
if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) &&
(b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) {
return true;
}
if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) {
return true;
}
if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) {
return true;
}
return false;
}
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
if ((unsigned int)pixel_format < 5) {
return SurfaceType::Color;
}
if ((unsigned int)pixel_format < 14) {
return SurfaceType::Texture;
}
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
return SurfaceType::Depth;
}
if (pixel_format == PixelFormat::D24S8) {
return SurfaceType::DepthStencil;
}
return SurfaceType::Invalid;
return OpenGL::GetFormatBpp(pixel_format);
}
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height"
@ -238,11 +63,11 @@ public:
}
u32 PixelsInBytes(u32 size) const {
return size * CHAR_BIT / GetFormatBpp(pixel_format);
return size * 8 / GetFormatBpp();
}
u32 BytesInPixels(u32 pixels) const {
return pixels * GetFormatBpp(pixel_format) / CHAR_BIT;
return pixels * GetFormatBpp() / 8;
}
bool ExactMatch(const SurfaceParams& other_surface) const;

@ -12,8 +12,6 @@
namespace OpenGL {
using PixelFormat = SurfaceParams::PixelFormat;
class RGBA4toRGB5A1 final : public FormatReinterpreterBase {
public:
RGBA4toRGB5A1() {

@ -9,27 +9,27 @@
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/rasterizer_cache/surface_params.h"
#include "video_core/rasterizer_cache/pixel_format.h"
namespace OpenGL {
class RasterizerCacheOpenGL;
struct PixelFormatPair {
const SurfaceParams::PixelFormat dst_format, src_format;
const PixelFormat dst_format, src_format;
struct less {
using is_transparent = void;
constexpr bool operator()(OpenGL::PixelFormatPair lhs, OpenGL::PixelFormatPair rhs) const {
constexpr bool operator()(PixelFormatPair lhs, PixelFormatPair rhs) const {
return std::tie(lhs.dst_format, lhs.src_format) <
std::tie(rhs.dst_format, rhs.src_format);
}
constexpr bool operator()(OpenGL::SurfaceParams::PixelFormat lhs,
OpenGL::PixelFormatPair rhs) const {
constexpr bool operator()(PixelFormat lhs, PixelFormatPair rhs) const {
return lhs < rhs.dst_format;
}
constexpr bool operator()(OpenGL::PixelFormatPair lhs,
OpenGL::SurfaceParams::PixelFormat rhs) const {
constexpr bool operator()(PixelFormatPair lhs, PixelFormat rhs) const {
return lhs.dst_format < rhs;
}
};
@ -52,8 +52,8 @@ public:
explicit FormatReinterpreterOpenGL();
~FormatReinterpreterOpenGL();
std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator> GetPossibleReinterpretations(
SurfaceParams::PixelFormat dst_format);
auto GetPossibleReinterpretations(PixelFormat dst_format) ->
std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator>;
private:
ReinterpreterMap reinterpreters;

@ -29,9 +29,6 @@
namespace OpenGL {
using PixelFormat = SurfaceParams::PixelFormat;
using SurfaceType = SurfaceParams::SurfaceType;
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(255, 128, 0));
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(192, 128, 128));
MICROPROFILE_DEFINE(OpenGL_GS, "OpenGL", "Geometry Shader Setup", MP_RGB(128, 192, 128));
@ -1445,7 +1442,7 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe
src_params.stride = config.input_width;
src_params.height = config.output_height;
src_params.is_tiled = !config.input_linear;
src_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.input_format);
src_params.pixel_format = PixelFormatFromGPUPixelFormat(config.input_format);
src_params.UpdateParams();
SurfaceParams dst_params;
@ -1455,7 +1452,7 @@ bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransfe
dst_params.height = config.scaling == config.ScaleXY ? config.output_height.Value() / 2
: config.output_height.Value();
dst_params.is_tiled = config.input_linear != config.dont_swizzle;
dst_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.output_format);
dst_params.pixel_format = PixelFormatFromGPUPixelFormat(config.output_format);
dst_params.UpdateParams();
Common::Rectangle<u32> src_rect;
@ -1595,7 +1592,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
src_params.height = config.height;
src_params.stride = pixel_stride;
src_params.is_tiled = false;
src_params.pixel_format = SurfaceParams::PixelFormatFromGPUPixelFormat(config.color_format);
src_params.pixel_format = PixelFormatFromGPUPixelFormat(config.color_format);
src_params.UpdateParams();
Common::Rectangle<u32> src_rect;

@ -4,9 +4,9 @@
#pragma once
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/rasterizer_cache/surface_params.h"
namespace OpenGL {

@ -60,12 +60,13 @@ bool TextureFilterer::IsNull() const {
bool TextureFilterer::Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& dst_rect,
SurfaceParams::SurfaceType type, GLuint read_fb_handle,
SurfaceType type, GLuint read_fb_handle,
GLuint draw_fb_handle) {
// depth / stencil texture filtering is not supported for now
if (IsNull() ||
(type != SurfaceParams::SurfaceType::Color && type != SurfaceParams::SurfaceType::Texture))
if (IsNull() || (type != SurfaceType::Color && type != SurfaceType::Texture)) {
return false;
}
filter->Filter(src_tex, src_rect, dst_tex, dst_rect, read_fb_handle, draw_fb_handle);
return true;
}
@ -85,4 +86,4 @@ std::vector<std::string_view> TextureFilterer::GetFilterNames() {
return ret;
}
} // namespace OpenGL
} // namespace OpenGL

@ -7,9 +7,7 @@
#include <memory>
#include <string_view>
#include <vector>
#include <glad/glad.h>
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/rasterizer_cache/pixel_format.h"
#include "video_core/renderer_opengl/texture_filters/texture_filter_base.h"
namespace OpenGL {
@ -25,7 +23,7 @@ public:
bool IsNull() const;
// returns true if the texture was able to be filtered
bool Filter(GLuint src_tex, const Common::Rectangle<u32>& src_rect, GLuint dst_tex,
const Common::Rectangle<u32>& dst_rect, SurfaceParams::SurfaceType type,
const Common::Rectangle<u32>& dst_rect, SurfaceType type,
GLuint read_fb_handle, GLuint draw_fb_handle);
static std::vector<std::string_view> GetFilterNames();