5fe5ccac42
pica/rasterizer: implement/stub texture wrap mode 4-7
452 lines
12 KiB
C++
452 lines
12 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;
|
|
/// @note Only valid for texture 0 according to 3DBrew.
|
|
BitField<28, 3, TextureType> type;
|
|
};
|
|
|
|
INSERT_PADDING_WORDS(0x1);
|
|
|
|
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 unsigned 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:
|
|
|
|
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<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;
|
|
}
|
|
|
|
INSERT_PADDING_WORDS(0x3);
|
|
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 {{
|
|
{main_config.texture0_enable.ToBool(), texture0, texture0_format},
|
|
{main_config.texture1_enable.ToBool(), texture1, texture1_format},
|
|
{main_config.texture2_enable.ToBool(), 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<11, 8, u32> width;
|
|
BitField<19, 8, u32> bias_high; // TODO: unimplemented
|
|
} proctex_lut;
|
|
|
|
BitField<0, 8, u32> 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
|