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 everywheremaster
parent
efc2db4088
commit
6a7d601e42
@ -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
|
Loading…
Reference in New Issue