gl_rasterizer: implement mipmap

This commit is contained in:
wwylele 2018-07-02 12:55:04 +03:00 committed by Weiyi Wang
parent acaca4188e
commit ca78d34933
6 changed files with 163 additions and 47 deletions

View file

@ -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;

View file

@ -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,13 @@ 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));
} }
if (wrap_s != config.wrap_s) { if (wrap_s != config.wrap_s) {
@ -1594,6 +1599,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() {

View file

@ -69,9 +69,13 @@ 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;
}; };
/// Structure that the hardware rendered vertices are composed of /// Structure that the hardware rendered vertices are composed of

View file

@ -1326,10 +1326,11 @@ 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) {
SurfaceParams params; SurfaceParams params;
params.addr = info.physical_address; params.addr = info.physical_address;
params.width = info.width; params.width = info.width;
@ -1338,23 +1339,97 @@ 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();
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) {

View file

@ -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
@ -434,7 +439,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);

View file

@ -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
if (gl_mode == 0) {
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index);
UNIMPLEMENTED();
return GL_LINEAR;
} }
LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", static_cast<u32>(mode));
UNIMPLEMENTED();
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) {