Merge pull request #3910 from wwylele/mipmap
gl_rasterizer: implement mipmap by forwarding PICA mipmap configuration
This commit is contained in:
commit
0ec45f694c
7 changed files with 217 additions and 51 deletions
|
@ -59,11 +59,16 @@ struct TexturingRegs {
|
||||||
BitField<2, 1, TextureFilter> min_filter;
|
BitField<2, 1, TextureFilter> min_filter;
|
||||||
BitField<8, 3, WrapMode> wrap_t;
|
BitField<8, 3, WrapMode> wrap_t;
|
||||||
BitField<12, 3, WrapMode> wrap_s;
|
BitField<12, 3, WrapMode> wrap_s;
|
||||||
|
BitField<24, 1, TextureFilter> mip_filter;
|
||||||
/// @note Only valid for texture 0 according to 3DBrew.
|
/// @note Only valid for texture 0 according to 3DBrew.
|
||||||
BitField<28, 3, TextureType> type;
|
BitField<28, 3, TextureType> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x1);
|
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;
|
BitField<0, 28, u32> address;
|
||||||
|
|
||||||
|
|
|
@ -1555,13 +1555,16 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
||||||
|
|
||||||
void RasterizerOpenGL::SamplerInfo::Create() {
|
void RasterizerOpenGL::SamplerInfo::Create() {
|
||||||
sampler.Create();
|
sampler.Create();
|
||||||
mag_filter = min_filter = TextureConfig::Linear;
|
mag_filter = min_filter = mip_filter = TextureConfig::Linear;
|
||||||
wrap_s = wrap_t = TextureConfig::Repeat;
|
wrap_s = wrap_t = TextureConfig::Repeat;
|
||||||
border_color = 0;
|
border_color = 0;
|
||||||
|
lod_min = lod_max = 0;
|
||||||
|
lod_bias = 0;
|
||||||
|
|
||||||
// default is GL_LINEAR_MIPMAP_LINEAR
|
// default is 1000 and -1000
|
||||||
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
// Other attributes have correct defaults
|
// Other attributes have correct defaults
|
||||||
|
glSamplerParameterf(sampler.handle, GL_TEXTURE_MAX_LOD, lod_max);
|
||||||
|
glSamplerParameterf(sampler.handle, GL_TEXTURE_MIN_LOD, lod_min);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||||
|
@ -1571,11 +1574,28 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||||
|
|
||||||
if (mag_filter != config.mag_filter) {
|
if (mag_filter != config.mag_filter) {
|
||||||
mag_filter = config.mag_filter;
|
mag_filter = config.mag_filter;
|
||||||
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter));
|
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureMagFilterMode(mag_filter));
|
||||||
}
|
}
|
||||||
if (min_filter != config.min_filter) {
|
if (min_filter != config.min_filter || mip_filter != config.mip_filter) {
|
||||||
min_filter = config.min_filter;
|
min_filter = config.min_filter;
|
||||||
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter));
|
mip_filter = config.mip_filter;
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||||
|
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(wwylele): remove this block once mipmap for cube is implemented
|
||||||
|
bool new_supress_mipmap_for_cube =
|
||||||
|
config.type == Pica::TexturingRegs::TextureConfig::TextureCube;
|
||||||
|
if (supress_mipmap_for_cube != new_supress_mipmap_for_cube) {
|
||||||
|
supress_mipmap_for_cube = new_supress_mipmap_for_cube;
|
||||||
|
if (new_supress_mipmap_for_cube) {
|
||||||
|
// HACK: use mag filter converter for min filter because they are the same anyway
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||||
|
PicaToGL::TextureMagFilterMode(min_filter));
|
||||||
|
} else {
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER,
|
||||||
|
PicaToGL::TextureMinFilterMode(min_filter, mip_filter));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrap_s != config.wrap_s) {
|
if (wrap_s != config.wrap_s) {
|
||||||
|
@ -1594,6 +1614,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
|
||||||
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data());
|
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lod_min != config.lod.min_level) {
|
||||||
|
lod_min = config.lod.min_level;
|
||||||
|
glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, lod_min);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lod_max != config.lod.max_level) {
|
||||||
|
lod_max = config.lod.max_level;
|
||||||
|
glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, lod_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lod_bias != config.lod.bias) {
|
||||||
|
lod_bias = config.lod.bias;
|
||||||
|
glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias / 256.0f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SetShader() {
|
void RasterizerOpenGL::SetShader() {
|
||||||
|
|
|
@ -69,9 +69,16 @@ private:
|
||||||
private:
|
private:
|
||||||
TextureConfig::TextureFilter mag_filter;
|
TextureConfig::TextureFilter mag_filter;
|
||||||
TextureConfig::TextureFilter min_filter;
|
TextureConfig::TextureFilter min_filter;
|
||||||
|
TextureConfig::TextureFilter mip_filter;
|
||||||
TextureConfig::WrapMode wrap_s;
|
TextureConfig::WrapMode wrap_s;
|
||||||
TextureConfig::WrapMode wrap_t;
|
TextureConfig::WrapMode wrap_t;
|
||||||
u32 border_color;
|
u32 border_color;
|
||||||
|
u32 lod_min;
|
||||||
|
u32 lod_max;
|
||||||
|
s32 lod_bias;
|
||||||
|
|
||||||
|
// TODO(wwylele): remove this once mipmap for cube is implemented
|
||||||
|
bool supress_mipmap_for_cube = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Structure that the hardware rendered vertices are composed of
|
/// Structure that the hardware rendered vertices are composed of
|
||||||
|
|
|
@ -1332,6 +1332,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
|
||||||
|
|
||||||
// Delete the expanded surface, this can't be done safely yet
|
// Delete the expanded surface, this can't be done safely yet
|
||||||
// because it may still be in use
|
// because it may still be in use
|
||||||
|
surface->UnlinkAllWatcher(); // unlink watchers as if this surface is already deleted
|
||||||
remove_surfaces.emplace(surface);
|
remove_surfaces.emplace(surface);
|
||||||
|
|
||||||
surface = new_surface;
|
surface = new_surface;
|
||||||
|
@ -1358,10 +1359,15 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(
|
||||||
const Pica::TexturingRegs::FullTextureConfig& config) {
|
const Pica::TexturingRegs::FullTextureConfig& config) {
|
||||||
Pica::Texture::TextureInfo info =
|
Pica::Texture::TextureInfo info =
|
||||||
Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format);
|
Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format);
|
||||||
return GetTextureSurface(info);
|
return GetTextureSurface(info, config.config.lod.max_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info) {
|
Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info,
|
||||||
|
u32 max_level) {
|
||||||
|
if (info.physical_address == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
SurfaceParams params;
|
SurfaceParams params;
|
||||||
params.addr = info.physical_address;
|
params.addr = info.physical_address;
|
||||||
params.width = info.width;
|
params.width = info.width;
|
||||||
|
@ -1370,23 +1376,100 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf
|
||||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
|
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
|
||||||
params.UpdateParams();
|
params.UpdateParams();
|
||||||
|
|
||||||
if (info.width % 8 != 0 || info.height % 8 != 0) {
|
u32 min_width = info.width >> max_level;
|
||||||
Surface src_surface;
|
u32 min_height = info.height >> max_level;
|
||||||
Common::Rectangle<u32> rect;
|
if (min_width % 8 != 0 || min_height % 8 != 0) {
|
||||||
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
|
LOG_CRITICAL(Render_OpenGL, "Texture size ({}x{}) is not multiple of 8", min_width,
|
||||||
|
min_height);
|
||||||
params.res_scale = src_surface->res_scale;
|
return nullptr;
|
||||||
Surface tmp_surface = CreateSurface(params);
|
}
|
||||||
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
|
if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) {
|
||||||
tmp_surface->GetScaledRect(),
|
LOG_CRITICAL(Render_OpenGL,
|
||||||
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
|
"Texture size ({}x{}) does not support required mipmap level ({})",
|
||||||
draw_framebuffer.handle);
|
params.width, params.height, max_level);
|
||||||
|
return nullptr;
|
||||||
remove_surfaces.emplace(tmp_surface);
|
|
||||||
return tmp_surface;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetSurface(params, ScaleMatch::Ignore, true);
|
auto surface = GetSurface(params, ScaleMatch::Ignore, true);
|
||||||
|
|
||||||
|
// Update mipmap if necessary
|
||||||
|
if (max_level != 0) {
|
||||||
|
if (max_level >= 8) {
|
||||||
|
// since PICA only supports texture size between 8 and 1024, there are at most eight
|
||||||
|
// possible mipmap levels including the base.
|
||||||
|
LOG_CRITICAL(Render_OpenGL, "Unsupported mipmap level {}", max_level);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||||
|
OpenGLState state;
|
||||||
|
SCOPE_EXIT({ prev_state.Apply(); });
|
||||||
|
auto format_tuple = GetFormatTuple(params.pixel_format);
|
||||||
|
|
||||||
|
// Allocate more mipmap level if necessary
|
||||||
|
if (surface->max_level < max_level) {
|
||||||
|
state.texture_units[0].texture_2d = surface->texture.handle;
|
||||||
|
state.Apply();
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
|
||||||
|
u32 width = surface->width * surface->res_scale;
|
||||||
|
u32 height = surface->height * surface->res_scale;
|
||||||
|
for (u32 level = surface->max_level + 1; level <= max_level; ++level) {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, level, format_tuple.internal_format, width >> level,
|
||||||
|
height >> level, 0, format_tuple.format, format_tuple.type, nullptr);
|
||||||
|
}
|
||||||
|
surface->max_level = max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blit mipmaps that have been invalidated
|
||||||
|
state.draw.read_framebuffer = read_framebuffer.handle;
|
||||||
|
state.draw.draw_framebuffer = draw_framebuffer.handle;
|
||||||
|
state.ResetTexture(surface->texture.handle);
|
||||||
|
SurfaceParams params = *surface;
|
||||||
|
for (u32 level = 1; level <= max_level; ++level) {
|
||||||
|
// In PICA all mipmap levels are stored next to each other
|
||||||
|
params.addr += params.width * params.height * params.GetFormatBpp() / 8;
|
||||||
|
params.width /= 2;
|
||||||
|
params.height /= 2;
|
||||||
|
params.stride = 0; // reset stride and let UpdateParams re-initialize it
|
||||||
|
params.UpdateParams();
|
||||||
|
auto& watcher = surface->level_watchers[level - 1];
|
||||||
|
if (!watcher || !watcher->Get()) {
|
||||||
|
auto level_surface = GetSurface(params, ScaleMatch::Ignore, true);
|
||||||
|
if (level_surface) {
|
||||||
|
watcher = level_surface->CreateWatcher();
|
||||||
|
} else {
|
||||||
|
watcher = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watcher && !watcher->IsValid()) {
|
||||||
|
auto level_surface = watcher->Get();
|
||||||
|
if (!level_surface->invalid_regions.empty()) {
|
||||||
|
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
|
||||||
|
}
|
||||||
|
state.ResetTexture(level_surface->texture.handle);
|
||||||
|
state.Apply();
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
|
level_surface->texture.handle, 0);
|
||||||
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
|
GL_TEXTURE_2D, 0, 0);
|
||||||
|
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
|
surface->texture.handle, level);
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
|
GL_TEXTURE_2D, 0, 0);
|
||||||
|
|
||||||
|
auto src_rect = level_surface->GetScaledRect();
|
||||||
|
auto dst_rect = params.GetScaledRect();
|
||||||
|
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top,
|
||||||
|
dst_rect.left, dst_rect.bottom, dst_rect.right, dst_rect.top,
|
||||||
|
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
|
watcher->Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCubeConfig& config) {
|
const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCubeConfig& config) {
|
||||||
|
@ -1456,6 +1539,9 @@ const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCube
|
||||||
for (const Face& face : faces) {
|
for (const Face& face : faces) {
|
||||||
if (face.watcher && !face.watcher->IsValid()) {
|
if (face.watcher && !face.watcher->IsValid()) {
|
||||||
auto surface = face.watcher->Get();
|
auto surface = face.watcher->Get();
|
||||||
|
if (!surface->invalid_regions.empty()) {
|
||||||
|
ValidateSurface(surface, surface->addr, surface->size);
|
||||||
|
}
|
||||||
state.ResetTexture(surface->texture.handle);
|
state.ResetTexture(surface->texture.handle);
|
||||||
state.Apply();
|
state.Apply();
|
||||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
|
@ -1764,6 +1850,7 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface
|
||||||
|
|
||||||
const auto interval = cached_surface->GetInterval() & invalid_interval;
|
const auto interval = cached_surface->GetInterval() & invalid_interval;
|
||||||
cached_surface->invalid_regions.insert(interval);
|
cached_surface->invalid_regions.insert(interval);
|
||||||
|
cached_surface->InvalidateAllWatcher();
|
||||||
|
|
||||||
// Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
|
// Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
|
||||||
if (cached_surface->type == SurfaceType::Fill &&
|
if (cached_surface->type == SurfaceType::Fill &&
|
||||||
|
|
|
@ -356,6 +356,11 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
|
||||||
|
|
||||||
OGLTexture texture;
|
OGLTexture texture;
|
||||||
|
|
||||||
|
/// max mipmap level that has been attached to the texture
|
||||||
|
u32 max_level = 0;
|
||||||
|
/// level_watchers[i] watches the (i+1)-th level mipmap source surface
|
||||||
|
std::array<std::shared_ptr<SurfaceWatcher>, 7> level_watchers;
|
||||||
|
|
||||||
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
|
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
|
||||||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
||||||
return format == PixelFormat::Invalid
|
return format == PixelFormat::Invalid
|
||||||
|
@ -392,6 +397,16 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnlinkAllWatcher() {
|
||||||
|
for (const auto& watcher : watchers) {
|
||||||
|
if (auto locked = watcher.lock()) {
|
||||||
|
locked->valid = false;
|
||||||
|
locked->surface.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watchers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<std::weak_ptr<SurfaceWatcher>> watchers;
|
std::list<std::weak_ptr<SurfaceWatcher>> watchers;
|
||||||
};
|
};
|
||||||
|
@ -434,7 +449,7 @@ public:
|
||||||
|
|
||||||
/// Get a surface based on the texture configuration
|
/// Get a surface based on the texture configuration
|
||||||
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||||
Surface GetTextureSurface(const Pica::Texture::TextureInfo& info);
|
Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
|
||||||
|
|
||||||
/// Get a texture cube based on the texture configuration
|
/// Get a texture cube based on the texture configuration
|
||||||
const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config);
|
const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config);
|
||||||
|
|
|
@ -317,8 +317,9 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||||
// Only unit 0 respects the texturing type
|
// Only unit 0 respects the texturing type
|
||||||
switch (state.texture0_type) {
|
switch (state.texture0_type) {
|
||||||
case TexturingRegs::TextureConfig::Texture2D:
|
case TexturingRegs::TextureConfig::Texture2D:
|
||||||
return "texture(tex0, texcoord0)";
|
return "textureLod(tex0, texcoord0, getLod(texcoord0 * textureSize(tex0, 0)))";
|
||||||
case TexturingRegs::TextureConfig::Projection2D:
|
case TexturingRegs::TextureConfig::Projection2D:
|
||||||
|
// TODO (wwylele): find the exact LOD formula for projection texture
|
||||||
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
|
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
|
||||||
case TexturingRegs::TextureConfig::TextureCube:
|
case TexturingRegs::TextureConfig::TextureCube:
|
||||||
return "texture(tex_cube, vec3(texcoord0, texcoord0_w))";
|
return "texture(tex_cube, vec3(texcoord0, texcoord0_w))";
|
||||||
|
@ -335,12 +336,12 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
||||||
return "texture(tex0, texcoord0)";
|
return "texture(tex0, texcoord0)";
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
return "texture(tex1, texcoord1)";
|
return "textureLod(tex1, texcoord1, getLod(texcoord1 * textureSize(tex1, 0)))";
|
||||||
case 2:
|
case 2:
|
||||||
if (state.texture2_use_coord1)
|
if (state.texture2_use_coord1)
|
||||||
return "texture(tex2, texcoord1)";
|
return "textureLod(tex2, texcoord1, getLod(texcoord1 * textureSize(tex2, 0)))";
|
||||||
else
|
else
|
||||||
return "texture(tex2, texcoord2)";
|
return "textureLod(tex2, texcoord2, getLod(texcoord2 * textureSize(tex2, 0)))";
|
||||||
case 3:
|
case 3:
|
||||||
if (state.proctex.enable) {
|
if (state.proctex.enable) {
|
||||||
return "ProcTex()";
|
return "ProcTex()";
|
||||||
|
@ -1333,6 +1334,15 @@ vec4 byteround(vec4 x) {
|
||||||
return round(x * 255.0) * (1.0 / 255.0);
|
return round(x * 255.0) * (1.0 / 255.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PICA's LOD formula for 2D textures.
|
||||||
|
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
||||||
|
// f(x, y) >= max{m_u, m_v, m_w}
|
||||||
|
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
|
||||||
|
float getLod(vec2 coord) {
|
||||||
|
vec2 d = max(abs(dFdx(coord)), abs(dFdy(coord)));
|
||||||
|
return log2(max(d.x, d.y));
|
||||||
|
}
|
||||||
|
|
||||||
#if ALLOW_SHADOW
|
#if ALLOW_SHADOW
|
||||||
|
|
||||||
uvec2 DecodeShadow(uint pixel) {
|
uvec2 DecodeShadow(uint pixel) {
|
||||||
|
|
|
@ -29,33 +29,40 @@ using GLivec4 = std::array<GLint, 4>;
|
||||||
|
|
||||||
namespace PicaToGL {
|
namespace PicaToGL {
|
||||||
|
|
||||||
inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) {
|
using TextureFilter = Pica::TexturingRegs::TextureConfig::TextureFilter;
|
||||||
static constexpr std::array<GLenum, 2> filter_mode_table{{
|
|
||||||
GL_NEAREST, // TextureFilter::Nearest
|
|
||||||
GL_LINEAR, // TextureFilter::Linear
|
|
||||||
}};
|
|
||||||
|
|
||||||
const auto index = static_cast<std::size_t>(mode);
|
|
||||||
|
|
||||||
// Range check table for input
|
|
||||||
if (index >= filter_mode_table.size()) {
|
|
||||||
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index);
|
|
||||||
UNREACHABLE();
|
|
||||||
|
|
||||||
|
inline GLenum TextureMagFilterMode(TextureFilter mode) {
|
||||||
|
if (mode == TextureFilter::Linear) {
|
||||||
return GL_LINEAR;
|
return GL_LINEAR;
|
||||||
}
|
}
|
||||||
|
if (mode == TextureFilter::Nearest) {
|
||||||
GLenum gl_mode = filter_mode_table[index];
|
return GL_NEAREST;
|
||||||
|
}
|
||||||
// Check for dummy values indicating an unknown mode
|
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", static_cast<u32>(mode));
|
||||||
if (gl_mode == 0) {
|
|
||||||
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index);
|
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
|
|
||||||
return GL_LINEAR;
|
return GL_LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gl_mode;
|
inline GLenum TextureMinFilterMode(TextureFilter min, TextureFilter mip) {
|
||||||
|
if (min == TextureFilter::Linear) {
|
||||||
|
if (mip == TextureFilter::Linear) {
|
||||||
|
return GL_LINEAR_MIPMAP_LINEAR;
|
||||||
|
}
|
||||||
|
if (mip == TextureFilter::Nearest) {
|
||||||
|
return GL_LINEAR_MIPMAP_NEAREST;
|
||||||
|
}
|
||||||
|
} else if (min == TextureFilter::Nearest) {
|
||||||
|
if (mip == TextureFilter::Linear) {
|
||||||
|
return GL_NEAREST_MIPMAP_LINEAR;
|
||||||
|
}
|
||||||
|
if (mip == TextureFilter::Nearest) {
|
||||||
|
return GL_NEAREST_MIPMAP_NEAREST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {} and {}", static_cast<u32>(min),
|
||||||
|
static_cast<u32>(mip));
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
return GL_LINEAR_MIPMAP_LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
|
inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
|
||||||
|
|
Loading…
Reference in a new issue