diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 6e18bb00c9..3c218d2d4f 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -131,6 +131,13 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) thin3d->SetViewports(1, &viewport); thin3d->SetBlendState(thin3d->GetBlendStatePreset(BS_OFF)); + Thin3DSamplerState *sampler; + if (g_Config.iBufFilter == SCALE_NEAREST) { + sampler = thin3d->GetSamplerStatePreset(T3DSamplerStatePreset::SAMPS_NEAREST); + } else { + sampler = thin3d->GetSamplerStatePreset(T3DSamplerStatePreset::SAMPS_LINEAR); + } + thin3d->SetSamplerStates(0, 1, &sampler); thin3d->SetDepthStencilState(depth); thin3d->SetRenderState(T3DRenderState::CULL_MODE, T3DCullMode::NO_CULL); thin3d->SetScissorEnabled(false); @@ -177,9 +184,6 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) } fbTex->Finalize(0); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, g_Config.iBufFilter == SCALE_NEAREST ? GL_NEAREST : GL_LINEAR); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, g_Config.iBufFilter == SCALE_NEAREST ? GL_NEAREST : GL_LINEAR); - float x, y, w, h; CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, dstwidth, dstheight, ROTATION_LOCKED_HORIZONTAL); diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index e72a8d67ae..78dd46a9d8 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -94,6 +94,12 @@ void Thin3DContext::CreatePresets() { bsPresets_[BS_STANDARD_ALPHA] = CreateBlendState(standard_alpha); bsPresets_[BS_PREMUL_ALPHA] = CreateBlendState(premul_alpha); + T3DSamplerStateDesc nearest = { CLAMP, CLAMP, NEAREST, NEAREST, NEAREST }; + T3DSamplerStateDesc linear = { CLAMP, CLAMP, LINEAR, LINEAR, NEAREST }; + + sampsPresets_[SAMPS_NEAREST] = CreateSamplerState(nearest); + sampsPresets_[SAMPS_LINEAR] = CreateSamplerState(linear); + vsPresets_[VS_TEXTURE_COLOR_2D] = CreateVertexShader(glsl_vsTexCol, hlslVsTexCol); vsPresets_[VS_COLOR_2D] = CreateVertexShader(glsl_vsCol, hlslVsCol); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 38d1bea866..bdf63c2120 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -66,6 +66,16 @@ enum T3DBlendFactor : int { FIXED_COLOR, }; +enum T3DTextureWrap : int { + REPEAT, + CLAMP, +}; + +enum T3DTextureFilter : int { + NEAREST, + LINEAR, +}; + enum T3DBufferUsage : int { VERTEXDATA = 1, INDEXDATA = 2, @@ -126,6 +136,12 @@ enum T3DBlendStatePreset : int { BS_MAX_PRESET, }; +enum T3DSamplerStatePreset : int { + SAMPS_NEAREST, + SAMPS_LINEAR, + SAMPS_MAX_PRESET, +}; + enum T3DClear : int { COLOR = 1, DEPTH = 2, @@ -207,6 +223,10 @@ class Thin3DBlendState : public Thin3DObject { public: }; +class Thin3DSamplerState : public Thin3DObject { +public: +}; + class Thin3DDepthStencilState : public Thin3DObject { public: }; @@ -278,12 +298,21 @@ struct T3DBlendStateDesc { // int colorMask; }; +struct T3DSamplerStateDesc { + T3DTextureWrap wrapS; + T3DTextureWrap wrapT; + T3DTextureFilter magFilt; + T3DTextureFilter minFilt; + T3DTextureFilter mipFilt; +}; + class Thin3DContext : public Thin3DObject { public: virtual ~Thin3DContext(); virtual Thin3DDepthStencilState *CreateDepthStencilState(bool depthTestEnabled, bool depthWriteEnabled, T3DComparison depthCompare) = 0; virtual Thin3DBlendState *CreateBlendState(const T3DBlendStateDesc &desc) = 0; + virtual Thin3DSamplerState *CreateSamplerState(const T3DSamplerStateDesc &desc) = 0; virtual Thin3DBuffer *CreateBuffer(size_t size, uint32_t usageFlags) = 0; virtual Thin3DShaderSet *CreateShaderSet(Thin3DShader *vshader, Thin3DShader *fshader) = 0; virtual Thin3DVertexFormat *CreateVertexFormat(const std::vector &components, int stride, Thin3DShader *vshader) = 0; @@ -297,6 +326,7 @@ public: // Note that these DO NOT AddRef so you must not ->Release presets unless you manually AddRef them. Thin3DBlendState *GetBlendStatePreset(T3DBlendStatePreset preset) { return bsPresets_[preset]; } + Thin3DSamplerState *GetSamplerStatePreset(T3DSamplerStatePreset preset) { return sampsPresets_[preset]; } Thin3DShader *GetVshaderPreset(T3DVertexShaderPreset preset) { return fsPresets_[preset]; } Thin3DShader *GetFshaderPreset(T3DFragmentShaderPreset preset) { return vsPresets_[preset]; } Thin3DShaderSet *GetShaderSetPreset(T3DShaderSetPreset preset) { return ssPresets_[preset]; } @@ -307,6 +337,7 @@ public: // Bound state objects. Too cumbersome to add them all as parameters to Draw. virtual void SetBlendState(Thin3DBlendState *state) = 0; + virtual void SetSamplerStates(int start, int count, Thin3DSamplerState **state) = 0; virtual void SetDepthStencilState(Thin3DDepthStencilState *state) = 0; virtual void SetTextures(int start, int count, Thin3DTexture **textures) = 0; @@ -343,6 +374,7 @@ protected: Thin3DShader *fsPresets_[FS_MAX_PRESET]; Thin3DBlendState *bsPresets_[BS_MAX_PRESET]; Thin3DShaderSet *ssPresets_[SS_MAX_PRESET]; + Thin3DSamplerState *sampsPresets_[SAMPS_MAX_PRESET]; int targetWidth_; int targetHeight_; diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 75ebba64dd..cc49f7d9c7 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -56,6 +56,16 @@ static const D3DBLEND blendFactorToD3D9[] = { D3DBLEND_BLENDFACTOR, }; +static const D3DTEXTUREADDRESS texWrapToD3D9[] = { + D3DTADDRESS_WRAP, + D3DTADDRESS_CLAMP, +}; + +static const D3DTEXTUREFILTERTYPE texFilterToD3D9[] = { + D3DTEXF_POINT, + D3DTEXF_LINEAR, +}; + static const D3DPRIMITIVETYPE primToD3D9[] = { D3DPT_POINTLIST, D3DPT_LINELIST, @@ -100,6 +110,20 @@ public: } }; +class Thin3DDX9SamplerState : public Thin3DSamplerState { +public: + D3DTEXTUREADDRESS wrapS, wrapT; + D3DTEXTUREFILTERTYPE magFilt, minFilt, mipFilt; + + void Apply(LPDIRECT3DDEVICE9 device, int index) { + device->SetSamplerState(index, D3DSAMP_ADDRESSU, wrapS); + device->SetSamplerState(index, D3DSAMP_ADDRESSV, wrapT); + device->SetSamplerState(index, D3DSAMP_MAGFILTER, magFilt); + device->SetSamplerState(index, D3DSAMP_MINFILTER, minFilt); + device->SetSamplerState(index, D3DSAMP_MIPFILTER, mipFilt); + } +}; + class Thin3DDX9Buffer : public Thin3DBuffer { public: Thin3DDX9Buffer(LPDIRECT3DDEVICE9 device, size_t size, uint32_t flags) : vbuffer_(nullptr), ibuffer_(nullptr), maxSize_(size) { @@ -403,6 +427,7 @@ public: Thin3DDepthStencilState *CreateDepthStencilState(bool depthTestEnabled, bool depthWriteEnabled, T3DComparison depthCompare); Thin3DBlendState *CreateBlendState(const T3DBlendStateDesc &desc) override; + Thin3DSamplerState *CreateSamplerState(const T3DSamplerStateDesc &desc) override; Thin3DBuffer *CreateBuffer(size_t size, uint32_t usageFlags) override; Thin3DShaderSet *CreateShaderSet(Thin3DShader *vshader, Thin3DShader *fshader) override; Thin3DVertexFormat *CreateVertexFormat(const std::vector &components, int stride, Thin3DShader *vshader) override; @@ -417,6 +442,12 @@ public: Thin3DDX9BlendState *bs = static_cast(state); bs->Apply(device_); } + void SetSamplerStates(int start, int count, Thin3DSamplerState **states) { + for (int i = 0; i < count; ++i) { + Thin3DDX9SamplerState *s = static_cast(states[start + i]); + s->Apply(device_, start + i); + } + } void SetDepthStencilState(Thin3DDepthStencilState *state) { Thin3DDX9DepthStencilState *bs = static_cast(state); bs->Apply(device_); @@ -528,6 +559,16 @@ Thin3DBlendState *Thin3DDX9Context::CreateBlendState(const T3DBlendStateDesc &de return bs; } +Thin3DSamplerState *Thin3DDX9Context::CreateSamplerState(const T3DSamplerStateDesc &desc) { + Thin3DDX9SamplerState *samps = new Thin3DDX9SamplerState(); + samps->wrapS = texWrapToD3D9[desc.wrapS]; + samps->wrapT = texWrapToD3D9[desc.wrapT]; + samps->magFilt = texFilterToD3D9[desc.magFilt]; + samps->minFilt = texFilterToD3D9[desc.minFilt]; + samps->mipFilt = texFilterToD3D9[desc.mipFilt]; + return samps; +} + Thin3DTexture *Thin3DDX9Context::CreateTexture() { Thin3DDX9Texture *tex = new Thin3DDX9Texture(device_, deviceEx_); return tex; @@ -631,12 +672,6 @@ void Thin3DDX9Context::Draw(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thin3 Thin3DDX9VertexFormat *fmt = static_cast(format); Thin3DDX9ShaderSet *ss = static_cast(shaderSet); - device_->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - device_->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - device_->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - device_->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - device_->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); - vbuf->BindAsVertexBuf(device_, fmt->GetStride(), offset); ss->Apply(device_); fmt->Apply(device_); @@ -660,12 +695,6 @@ void Thin3DDX9Context::DrawUP(T3DPrimitive prim, Thin3DShaderSet *shaderSet, Thi Thin3DDX9VertexFormat *fmt = static_cast(format); Thin3DDX9ShaderSet *ss = static_cast(shaderSet); - device_->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - device_->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - device_->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - device_->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - device_->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); - ss->Apply(device_); fmt->Apply(device_); device_->DrawPrimitiveUP(primToD3D9[prim], vertexCount / 3, vdata, fmt->GetStride()); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 920927f9e7..141ed0b49d 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -42,6 +42,23 @@ static const unsigned short blendFactorToGL[] = { GL_CONSTANT_COLOR, }; +static const unsigned short texWrapToGL[] = { + GL_REPEAT, + GL_CLAMP_TO_EDGE, +}; + +static const unsigned short texFilterToGL[] = { + GL_NEAREST, + GL_LINEAR, +}; + +static const unsigned short texMipFilterToGL[2][2] = { + // Min nearest: + { GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR }, + // Min linear: + { GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR }, +}; + #ifndef USING_GLES2 static const unsigned short logicOpToGL[] = { GL_CLEAR, @@ -114,6 +131,32 @@ public: } }; +class Thin3DGLSamplerState : public Thin3DSamplerState { +public: + GLint wrapS; + GLint wrapT; + GLint magFilt; + GLint minFilt; + GLint mipMinFilt; + + void Apply(bool hasMips, bool canWrap) { + if (canWrap) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilt); + if (hasMips) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMinFilt); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilt); + } + } +}; + class Thin3DGLDepthStencilState : public Thin3DDepthStencilState { public: bool depthTestEnabled; @@ -307,6 +350,7 @@ public: Thin3DDepthStencilState *CreateDepthStencilState(bool depthTestEnabled, bool depthWriteEnabled, T3DComparison depthCompare) override; Thin3DBlendState *CreateBlendState(const T3DBlendStateDesc &desc) override; + Thin3DSamplerState *CreateSamplerState(const T3DSamplerStateDesc &desc) override; Thin3DBuffer *CreateBuffer(size_t size, uint32_t usageFlags) override; Thin3DShaderSet *CreateShaderSet(Thin3DShader *vshader, Thin3DShader *fshader) override; Thin3DVertexFormat *CreateVertexFormat(const std::vector &components, int stride, Thin3DShader *vshader) override; @@ -319,6 +363,28 @@ public: s->Apply(); } + void SetSamplerStates(int start, int count, Thin3DSamplerState **states) override { + if (samplerStates_.size() < (size_t)(start + count)) { + samplerStates_.resize(start + count); + } + for (int i = 0; i < count; ++i) { + int index = i + start; + Thin3DGLSamplerState *s = static_cast(states[index]); + + if (samplerStates_[index]) { + samplerStates_[index]->Release(); + } + samplerStates_[index] = s; + samplerStates_[index]->AddRef(); + + // TODO: Ideally, get these from the texture and apply on the right stage? + if (index == 0) { + s->Apply(false, true); + } + } + + } + // Bound state objects void SetDepthStencilState(Thin3DDepthStencilState *state) override { Thin3DGLDepthStencilState *s = static_cast(state); @@ -391,6 +457,8 @@ public: default: return "?"; } } + + std::vector samplerStates_; }; Thin3DGLContext::Thin3DGLContext() { @@ -398,6 +466,12 @@ Thin3DGLContext::Thin3DGLContext() { } Thin3DGLContext::~Thin3DGLContext() { + for (Thin3DGLSamplerState *s : samplerStates_) { + if (s) { + s->Release(); + } + } + samplerStates_.clear(); } Thin3DVertexFormat *Thin3DGLContext::CreateVertexFormat(const std::vector &components, int stride, Thin3DShader *vshader) { @@ -428,6 +502,7 @@ class Thin3DGLTexture : public Thin3DTexture, GfxResourceHolder { public: Thin3DGLTexture() : tex_(0), target_(0) { generatedMips_ = false; + canWrap_ = true; width_ = 0; height_ = 0; depth_ = 0; @@ -436,6 +511,7 @@ public: } Thin3DGLTexture(T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels) : tex_(0), target_(TypeToTarget(type)), format_(format), mipLevels_(mipLevels) { generatedMips_ = false; + canWrap_ = true; width_ = width; height_ = height; depth_ = depth; @@ -449,6 +525,7 @@ public: bool Create(T3DTextureType type, T3DImageFormat format, int width, int height, int depth, int mipLevels) override { generatedMips_ = false; + canWrap_ = true; format_ = format; target_ = TypeToTarget(type); mipLevels_ = mipLevels; @@ -468,6 +545,13 @@ public: void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) override; void AutoGenMipmaps() override; + bool HasMips() { + return mipLevels_ > 1 || generatedMips_; + } + bool CanWrap() { + return canWrap_; + } + void Bind() { glBindTexture(target_, tex_); } @@ -494,6 +578,7 @@ private: T3DImageFormat format_; int mipLevels_; bool generatedMips_; + bool canWrap_; }; Thin3DTexture *Thin3DGLContext::CreateTexture() { @@ -508,6 +593,7 @@ void Thin3DGLTexture::AutoGenMipmaps() { if (!generatedMips_) { Bind(); glGenerateMipmap(target_); + // TODO: Really, this should follow the sampler state. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); generatedMips_ = true; } @@ -553,17 +639,7 @@ bool isPowerOf2(int n) { } void Thin3DGLTexture::Finalize(int zim_flags) { - GLenum wrap = GL_REPEAT; - if ((zim_flags & ZIM_CLAMP) || !isPowerOf2(width_) || !isPowerOf2(height_)) - wrap = GL_CLAMP_TO_EDGE; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - if ((zim_flags & (ZIM_HAS_MIPS | ZIM_GEN_MIPS))) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } + canWrap_ = (zim_flags & ZIM_CLAMP) || !isPowerOf2(width_) || !isPowerOf2(height_); } @@ -618,6 +694,16 @@ Thin3DBlendState *Thin3DGLContext::CreateBlendState(const T3DBlendStateDesc &des return bs; } +Thin3DSamplerState *Thin3DGLContext::CreateSamplerState(const T3DSamplerStateDesc &desc) { + Thin3DGLSamplerState *samps = new Thin3DGLSamplerState(); + samps->wrapS = texWrapToGL[desc.wrapS]; + samps->wrapT = texWrapToGL[desc.wrapT]; + samps->magFilt = texFilterToGL[desc.magFilt]; + samps->minFilt = texFilterToGL[desc.minFilt]; + samps->mipMinFilt = texMipFilterToGL[desc.minFilt][desc.mipFilt]; + return samps; +} + Thin3DBuffer *Thin3DGLContext::CreateBuffer(size_t size, uint32_t usageFlags) { return new Thin3DGLBuffer(size, usageFlags); } @@ -645,6 +731,10 @@ void Thin3DGLContext::SetTextures(int start, int count, Thin3DTexture **textures Thin3DGLTexture *glTex = static_cast(textures[i]); glActiveTexture(GL_TEXTURE0 + i); glTex->Bind(); + + if (samplerStates_.size() > i && samplerStates_[i]) { + samplerStates_[i]->Apply(glTex->HasMips(), glTex->CanWrap()); + } } glActiveTexture(GL_TEXTURE0); } diff --git a/ext/native/ui/ui_context.cpp b/ext/native/ui/ui_context.cpp index d2c2d319b6..526047a33f 100644 --- a/ext/native/ui/ui_context.cpp +++ b/ext/native/ui/ui_context.cpp @@ -23,6 +23,7 @@ UIContext::~UIContext() { void UIContext::Init(Thin3DContext *thin3d, Thin3DShaderSet *uishader, Thin3DShaderSet *uishadernotex, Thin3DTexture *uitexture, DrawBuffer *uidrawbuffer, DrawBuffer *uidrawbufferTop) { thin3d_ = thin3d; blend_ = thin3d_->GetBlendStatePreset(T3DBlendStatePreset::BS_STANDARD_ALPHA); + sampler_ = thin3d_->GetSamplerStatePreset(T3DSamplerStatePreset::SAMPS_LINEAR); depth_ = thin3d_->CreateDepthStencilState(false, false, T3DComparison::LESS); uishader_ = uishader; @@ -39,6 +40,7 @@ void UIContext::Init(Thin3DContext *thin3d, Thin3DShaderSet *uishader, Thin3DSha void UIContext::Begin() { thin3d_->SetBlendState(blend_); + thin3d_->SetSamplerStates(0, 1, &sampler_); thin3d_->SetDepthStencilState(depth_); thin3d_->SetRenderState(T3DRenderState::CULL_MODE, T3DCullMode::NO_CULL); thin3d_->SetTexture(0, uitexture_); @@ -48,6 +50,7 @@ void UIContext::Begin() { void UIContext::BeginNoTex() { thin3d_->SetBlendState(blend_); + thin3d_->SetSamplerStates(0, 1, &sampler_); thin3d_->SetRenderState(T3DRenderState::CULL_MODE, T3DCullMode::NO_CULL); UIBegin(uishadernotex_); diff --git a/ext/native/ui/ui_context.h b/ext/native/ui/ui_context.h index 628a96cba1..3fba861d68 100644 --- a/ext/native/ui/ui_context.h +++ b/ext/native/ui/ui_context.h @@ -14,6 +14,7 @@ class Thin3DShaderSet; class Thin3DDepthStencilState; class Thin3DTexture; class Thin3DBlendState; +class Thin3DSamplerState; class Texture; class DrawBuffer; class TextDrawer; @@ -82,6 +83,7 @@ private: Thin3DContext *thin3D_; Thin3DDepthStencilState *depth_; Thin3DBlendState *blend_; + Thin3DSamplerState *sampler_; Thin3DShaderSet *uishader_; Thin3DShaderSet *uishadernotex_; Thin3DTexture *uitexture_;