citra/src/video_core/regs_texturing.h
GPUCode 26d6f9d1c6
Rasterizer cache refactor (#6375)
* rasterizer_cache: Remove custom texture code

* It's a hacky buggy mess, will be reimplemented later when the cache is in a better state

* rasterizer_cache: Refactor surface upload/download

* Switch to the texture_codec header which was written as part of the vulkan backend by steveice and me

* Move most of the upload logic to the rasterizer cache and out of the surface object

* Scaled uploads/downloads have been disabled for now since they require more runtime infrastructure

* rasterizer_cache: Refactor runtime interface

* Remove aspect enum which is the same as SurfaceType

* Replace Subresource with specific structures for each operation (blit/copy/clear). This mimics moderns APIs vulkan much better

* Pass the surface to the runtime instead of the texture

* Implement CopyTextures with glCopyImageSubData which is available on 4.3 and gles.
  This function also has an overload for cubes which will be removed later.

* rasterizer_cache: Move texture allocation to the runtime

* renderer_opengl: Remove TextureDownloaderES

* It's overly compilcated and unused at the moment. Will be replaced with a simple compute shader in a later commit

* rasterizer_cache: Split CachedSurface

* This commit splits CachedSurface into two classes, SurfaceBase which contains the backend agnostic functions and Surface which is the opengl specific part

* For now the cache uses the opengl surface directly and there are a few ugly casts with watchers, those will be taken care of when the template convertion and watcher removal are added respectively

* rasterizer_cache: Move reinterpreters to the runtime

* rasterizer_cache: Move some pixel format function to the cpp file

* rasterizer_cache: Common texture acceleration functions

* They don't contain any backend specific code so they shouldn't be duplicated

* rasterizer_cache: Remove BlitSurfaces

* It's better to prefer copy/blit in the caller anyway

* rasterizer_cache: Only allocate needed levels

* rasterizer_cache: Move texture runtime out of common dir

* Also shorten the util header filename

* surface_params: Cleanup code

* Add more comments, organize it a bit etc

* rasterizer_cache: Move texture filtering to the runtime

* rasterizer_cache: Move to VideoCore

* renderer_opengl: Reimplement scaled uploads/downloads

* Instead of looking up for temporary textures, each allocation now contains both a scaled and unscaled handle
  This allows the scale operations to be done inside the surface object itself and improves performance in general

* In particular the scaled download code has been expanded to use ARB_get_texture_sub_image when possible
  which is faster and more convenient than glReadPixels. The latter is still relevant for OpenGLES though.

* Finally allocations are now given a handy debug name that can be viewed from renderdoc.

* rasterizer_cache: Remove global state

* gl_rasterizer: Abstract common draw operations to Framebuffer

* This also allows to cache framebuffer objects instead of always swapping the textures, something that particularly benefits mali gpus

* rasterizer_cache: Implement multi-level surfaces

* With this commit the cache can now directly upload and use mipmaps
  without needing to sync them with watchers. By using native mimaps
  directly this also adds support for mipmap for cube

* Texture cubes have also been updated to drop the watcher requirement

* host_shaders: Add CMake integration for string shaders

* Improves build time shader generation making it much less prone to errors.
  Also moves the presentation shaders here to avoid embedding them to the cpp file.

* Texture filter shaders now make explicit use of uniform bindings for better vulkan compatibility

* renderer_opengl: Emulate lod bias in the shader

* This way opengles can emulate it correctly

* gl_rasterizer: Respect GL_MAX_TEXTURE_BUFFER_SIZE

* Older Bifrost Mali GPUs only support up to 64kb texture buffers. Citra would try to allocate a much larger buffer the first 64kb of which would work fine but after that the driver starts misbehaving and showing various graphical glitches

* rasterizer_cache: Cleanup CopySurface

* renderer_opengl: Keep frames synchronized when using a GPU debugger

* rasterizer_cache: Rename Surface to SurfaceRef

* Makes it clear that surface is a shared_ptr and not an object

* rasterizer_cache: Cleanup

* Move constructor to the top of the file

* Move FindMatch to the top as well and remove the Invalid flag which was redudant;
  all FindMatch calls used it expect from MatchFlags::Copy which ignores it anyway

* gl_texture_runtime: Make driver const

* gl_texture_runtime: Fix RGB8 format handling

* The texture_codec header, being written with vulkan in mind converts RGB8 to RGBA8. The backend wasn't adjusted to account for this though and treated the data as RGB8.

* Also remove D16 convertions, both opengl and vulkan are required to support this format so these are not needed

* gl_texture_runtime: Reduce state switches during FBO blits

* glBlitFramebuffer is only affected by the scissor rectangle so just disable scissor testing instead of resetting our entire state

* surface_params: Prevent texcopy that spans multiple levels

* It would have failed before as well, with multi-level surfaces it triggers the assert though

* renderer_opengl: Centralize texture filters

* A lot of code is shared between the filters thus is makes it sense to centralize them

* Also fix an issue with partial texture uploads

* Address review comments

* rasterizer_cache: Use leading return types

* rasterizer_cache: Cleanup null checks

* renderer_opengl: Add additional logging

* externals: Actually downgrade glad

* For some reason I missed adding the files to git

* surface_params: Do not check for levels in exact match

* Some games will try to use the base level of a multi level surface. Checking for levels forces another surface to be created and a copy to be made which is both unncessary and breaks custom textures

---------

Co-authored-by: bunnei <bunneidev@gmail.com>
2023-04-21 10:14:55 +03:00

466 lines
13 KiB
C++

// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Pica {
struct TexturingRegs {
struct TextureConfig {
enum TextureType : u32 {
Texture2D = 0,
TextureCube = 1,
Shadow2D = 2,
Projection2D = 3,
ShadowCube = 4,
Disabled = 5,
};
enum WrapMode : u32 {
ClampToEdge = 0,
ClampToBorder = 1,
Repeat = 2,
MirroredRepeat = 3,
// Mode 4-7 produces some weird result and may be just invalid:
ClampToEdge2 = 4, // Positive coord: clamp to edge; negative coord: repeat
ClampToBorder2 = 5, // Positive coord: clamp to border; negative coord: repeat
Repeat2 = 6, // Same as Repeat
Repeat3 = 7, // Same as Repeat
};
enum TextureFilter : u32 {
Nearest = 0,
Linear = 1,
};
union {
u32 raw;
BitField<0, 8, u32> r;
BitField<8, 8, u32> g;
BitField<16, 8, u32> b;
BitField<24, 8, u32> a;
} border_color;
union {
BitField<0, 11, u32> height;
BitField<16, 11, u32> width;
};
union {
BitField<1, 1, TextureFilter> mag_filter;
BitField<2, 1, TextureFilter> min_filter;
BitField<8, 3, WrapMode> wrap_t;
BitField<12, 3, WrapMode> wrap_s;
BitField<24, 1, TextureFilter> mip_filter;
/// @note Only valid for texture 0 according to 3DBrew.
BitField<28, 3, TextureType> type;
};
union {
BitField<0, 13, s32> bias; // fixed1.4.8
BitField<16, 4, u32> max_level;
BitField<24, 4, u32> min_level;
} lod;
BitField<0, 28, u32> address;
PAddr GetPhysicalAddress() const {
return address * 8;
}
// texture1 and texture2 store the texture format directly after the address
// whereas texture0 inserts some additional flags inbetween.
// Hence, we store the format separately so that all other parameters can be described
// in a single structure.
};
enum class TextureFormat : u32 {
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
IA8 = 5,
RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12, // compressed
ETC1A4 = 13, // compressed
};
static u32 NibblesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::RGBA8:
return 8;
case TextureFormat::RGB8:
return 6;
case TextureFormat::RGB5A1:
case TextureFormat::RGB565:
case TextureFormat::RGBA4:
case TextureFormat::IA8:
case TextureFormat::RG8:
return 4;
case TextureFormat::I4:
case TextureFormat::A4:
return 1;
case TextureFormat::I8:
case TextureFormat::A8:
case TextureFormat::IA4:
return 2;
default: // placeholder for yet unknown formats
UNIMPLEMENTED();
return 0;
}
}
union {
BitField<0, 1, u32> texture0_enable;
BitField<1, 1, u32> texture1_enable;
BitField<2, 1, u32> texture2_enable;
BitField<8, 2, u32> texture3_coordinates;
BitField<10, 1, u32> texture3_enable;
BitField<13, 1, u32> texture2_use_coord1;
BitField<16, 1, u32> clear_texture_cache; // TODO: unimplemented
} main_config;
TextureConfig texture0;
enum class CubeFace {
PositiveX = 0,
NegativeX = 1,
PositiveY = 2,
NegativeY = 3,
PositiveZ = 4,
NegativeZ = 5,
};
BitField<0, 22, u32> cube_address[5];
PAddr GetCubePhysicalAddress(CubeFace face) const {
PAddr address = texture0.address;
if (face != CubeFace::PositiveX) {
// Bits [22:27] from the main texture address is shared with all cubemap additional
// addresses.
auto& face_addr = cube_address[static_cast<std::size_t>(face) - 1];
address &= ~face_addr.mask;
address |= face_addr;
}
// A multiplier of 8 is also needed in the same way as the main address.
return address * 8;
}
union {
BitField<0, 1, u32> orthographic; // 0: enable perspective divide
BitField<1, 23, u32> bias; // 23-bit fraction
} shadow;
INSERT_PADDING_WORDS(0x2);
BitField<0, 4, TextureFormat> texture0_format;
BitField<0, 1, u32> fragment_lighting_enable;
INSERT_PADDING_WORDS(0x1);
TextureConfig texture1;
BitField<0, 4, TextureFormat> texture1_format;
INSERT_PADDING_WORDS(0x2);
TextureConfig texture2;
BitField<0, 4, TextureFormat> texture2_format;
INSERT_PADDING_WORDS(0x9);
struct FullTextureConfig {
const bool enabled;
const TextureConfig config;
const TextureFormat format;
};
const std::array<FullTextureConfig, 3> GetTextures() const {
return {{
{static_cast<bool>(main_config.texture0_enable), texture0, texture0_format},
{static_cast<bool>(main_config.texture1_enable), texture1, texture1_format},
{static_cast<bool>(main_config.texture2_enable), texture2, texture2_format},
}};
}
// 0xa8-0xad: ProcTex Config
enum class ProcTexClamp : u32 {
ToZero = 0,
ToEdge = 1,
SymmetricalRepeat = 2,
MirroredRepeat = 3,
Pulse = 4,
};
enum class ProcTexCombiner : u32 {
U = 0, // u
U2 = 1, // u * u
V = 2, // v
V2 = 3, // v * v
Add = 4, // (u + v) / 2
Add2 = 5, // (u * u + v * v) / 2
SqrtAdd2 = 6, // sqrt(u * u + v * v)
Min = 7, // min(u, v)
Max = 8, // max(u, v)
RMax = 9, // Average of Max and SqrtAdd2
};
enum class ProcTexShift : u32 {
None = 0,
Odd = 1,
Even = 2,
};
union {
BitField<0, 3, ProcTexClamp> u_clamp;
BitField<3, 3, ProcTexClamp> v_clamp;
BitField<6, 4, ProcTexCombiner> color_combiner;
BitField<10, 4, ProcTexCombiner> alpha_combiner;
BitField<14, 1, u32> separate_alpha;
BitField<15, 1, u32> noise_enable;
BitField<16, 2, ProcTexShift> u_shift;
BitField<18, 2, ProcTexShift> v_shift;
BitField<20, 8, u32> bias_low; // float16 TODO: unimplemented
} proctex;
union ProcTexNoiseConfig {
BitField<0, 16, s32> amplitude; // fixed1.3.12
BitField<16, 16, u32> phase; // float16
};
ProcTexNoiseConfig proctex_noise_u;
ProcTexNoiseConfig proctex_noise_v;
union {
BitField<0, 16, u32> u; // float16
BitField<16, 16, u32> v; // float16
} proctex_noise_frequency;
enum class ProcTexFilter : u32 {
Nearest = 0,
Linear = 1,
NearestMipmapNearest = 2,
LinearMipmapNearest = 3,
NearestMipmapLinear = 4,
LinearMipmapLinear = 5,
};
union {
BitField<0, 3, ProcTexFilter> filter;
BitField<3, 4, u32> lod_min;
BitField<7, 4, u32> lod_max;
BitField<11, 8, u32> width;
BitField<19, 8, u32> bias_high; // TODO: unimplemented
} proctex_lut;
union {
BitField<0, 8, u32> level0;
BitField<8, 8, u32> level1;
BitField<16, 8, u32> level2;
BitField<24, 8, u32> level3;
} proctex_lut_offset;
INSERT_PADDING_WORDS(0x1);
// 0xaf-0xb7: ProcTex LUT
enum class ProcTexLutTable : u32 {
Noise = 0,
ColorMap = 2,
AlphaMap = 3,
Color = 4,
ColorDiff = 5,
};
union {
BitField<0, 8, u32> index;
BitField<8, 4, ProcTexLutTable> ref_table;
} proctex_lut_config;
u32 proctex_lut_data[8];
INSERT_PADDING_WORDS(0x8);
// 0xc0-0xff: Texture Combiner (akin to glTexEnv)
struct TevStageConfig {
enum class Source : u32 {
PrimaryColor = 0x0,
PrimaryFragmentColor = 0x1,
SecondaryFragmentColor = 0x2,
Texture0 = 0x3,
Texture1 = 0x4,
Texture2 = 0x5,
Texture3 = 0x6,
PreviousBuffer = 0xd,
Constant = 0xe,
Previous = 0xf,
};
enum class ColorModifier : u32 {
SourceColor = 0x0,
OneMinusSourceColor = 0x1,
SourceAlpha = 0x2,
OneMinusSourceAlpha = 0x3,
SourceRed = 0x4,
OneMinusSourceRed = 0x5,
SourceGreen = 0x8,
OneMinusSourceGreen = 0x9,
SourceBlue = 0xc,
OneMinusSourceBlue = 0xd,
};
enum class AlphaModifier : u32 {
SourceAlpha = 0x0,
OneMinusSourceAlpha = 0x1,
SourceRed = 0x2,
OneMinusSourceRed = 0x3,
SourceGreen = 0x4,
OneMinusSourceGreen = 0x5,
SourceBlue = 0x6,
OneMinusSourceBlue = 0x7,
};
enum class Operation : u32 {
Replace = 0,
Modulate = 1,
Add = 2,
AddSigned = 3,
Lerp = 4,
Subtract = 5,
Dot3_RGB = 6,
Dot3_RGBA = 7,
MultiplyThenAdd = 8,
AddThenMultiply = 9,
};
union {
u32 sources_raw;
BitField<0, 4, Source> color_source1;
BitField<4, 4, Source> color_source2;
BitField<8, 4, Source> color_source3;
BitField<16, 4, Source> alpha_source1;
BitField<20, 4, Source> alpha_source2;
BitField<24, 4, Source> alpha_source3;
};
union {
u32 modifiers_raw;
BitField<0, 4, ColorModifier> color_modifier1;
BitField<4, 4, ColorModifier> color_modifier2;
BitField<8, 4, ColorModifier> color_modifier3;
BitField<12, 3, AlphaModifier> alpha_modifier1;
BitField<16, 3, AlphaModifier> alpha_modifier2;
BitField<20, 3, AlphaModifier> alpha_modifier3;
};
union {
u32 ops_raw;
BitField<0, 4, Operation> color_op;
BitField<16, 4, Operation> alpha_op;
};
union {
u32 const_color;
BitField<0, 8, u32> const_r;
BitField<8, 8, u32> const_g;
BitField<16, 8, u32> const_b;
BitField<24, 8, u32> const_a;
};
union {
u32 scales_raw;
BitField<0, 2, u32> color_scale;
BitField<16, 2, u32> alpha_scale;
};
inline unsigned GetColorMultiplier() const {
return (color_scale < 3) ? (1 << color_scale) : 1;
}
inline unsigned GetAlphaMultiplier() const {
return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
}
};
TevStageConfig tev_stage0;
INSERT_PADDING_WORDS(0x3);
TevStageConfig tev_stage1;
INSERT_PADDING_WORDS(0x3);
TevStageConfig tev_stage2;
INSERT_PADDING_WORDS(0x3);
TevStageConfig tev_stage3;
INSERT_PADDING_WORDS(0x3);
enum class FogMode : u32 {
None = 0,
Fog = 5,
Gas = 7,
};
union {
BitField<0, 3, FogMode> fog_mode;
BitField<16, 1, u32> fog_flip;
union {
// Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
// these masks are set
BitField<8, 4, u32> update_mask_rgb;
BitField<12, 4, u32> update_mask_a;
bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
}
bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
return (stage_index < 4) && (update_mask_a & (1 << stage_index));
}
} tev_combiner_buffer_input;
};
union {
u32 raw;
BitField<0, 8, u32> r;
BitField<8, 8, u32> g;
BitField<16, 8, u32> b;
} fog_color;
INSERT_PADDING_WORDS(0x4);
BitField<0, 16, u32> fog_lut_offset;
INSERT_PADDING_WORDS(0x1);
u32 fog_lut_data[8];
TevStageConfig tev_stage4;
INSERT_PADDING_WORDS(0x3);
TevStageConfig tev_stage5;
union {
u32 raw;
BitField<0, 8, u32> r;
BitField<8, 8, u32> g;
BitField<16, 8, u32> b;
BitField<24, 8, u32> a;
} tev_combiner_buffer_color;
INSERT_PADDING_WORDS(0x2);
const std::array<TevStageConfig, 6> GetTevStages() const {
return {{tev_stage0, tev_stage1, tev_stage2, tev_stage3, tev_stage4, tev_stage5}};
};
};
static_assert(sizeof(TexturingRegs) == 0x80 * sizeof(u32),
"TexturingRegs struct has incorrect size");
} // namespace Pica