diff --git a/CMakeLists.txt b/CMakeLists.txt index dad23f4d8d..028b59e23e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1269,6 +1269,7 @@ add_library(GPU OBJECT GPU/GLES/Spline.cpp GPU/GLES/StateMapping.cpp GPU/GLES/StateMapping.h + GPU/GLES/StencilBuffer.cpp GPU/GLES/TextureCache.cpp GPU/GLES/TextureCache.h GPU/GLES/TextureScaler.cpp diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index d4c8ac42da..5eb2c7626a 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -20,6 +20,7 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "Core/Config.h" #include "Core/Debugger/Breakpoints.h" #include "Core/MemMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" @@ -111,8 +112,20 @@ static int Replace_memcpy() { } if (!skip && bytes != 0) { u8 *dst = Memory::GetPointerUnchecked(destPtr); - u8 *src = Memory::GetPointerUnchecked(srcPtr); - memmove(dst, src, bytes); + const u8 *src = Memory::GetPointerUnchecked(srcPtr); + + if (std::min(destPtr, srcPtr) + bytes > std::max(destPtr, srcPtr)) { + // Overlap. Star Ocean breaks if it's not handled in 16 bytes blocks. + const u32 blocks = bytes & ~0x0f; + for (u32 offset = 0; offset < blocks; offset += 0x10) { + memcpy(dst + offset, src + offset, 0x10); + } + for (u32 offset = blocks; offset < bytes; ++offset) { + dst[offset] = src[offset]; + } + } else { + memmove(dst, src, bytes); + } } RETURN(destPtr); #ifndef MOBILE_DEVICE @@ -483,6 +496,14 @@ static int Hook_hexyzforce_monoclome_thread() { return 0; } +static int Hook_starocean_write_stencil() { + u32 fb_address = currentMIPS->r[MIPS_REG_T7]; + if (Memory::IsVRAMAddress(fb_address) && !g_Config.bDisableStencilTest) { + gpu->PerformStencilUpload(fb_address, 0x00088000); + } + return 0; +} + // Can either replace with C functions or functions emitted in Asm/ArmAsm. static const ReplacementTableEntry entries[] = { // TODO: I think some games can be helped quite a bit by implementing the @@ -524,6 +545,7 @@ static const ReplacementTableEntry entries[] = { { "godseaterburst_blit_texture", &Hook_godseaterburst_blit_texture, 0, REPFLAG_HOOKENTER}, { "hexyzforce_monoclome_thread", &Hook_hexyzforce_monoclome_thread, 0, REPFLAG_HOOKENTER, 0x58}, + { "starocean_write_stencil", &Hook_starocean_write_stencil, 0, REPFLAG_HOOKENTER, 0x260}, {} }; @@ -586,7 +608,7 @@ void WriteReplaceInstructions(u32 address, u64 hash, int size) { for (u32 offset = 0; offset < (u32)size; offset += 4) { const u32 op = Memory::Read_U32(address + offset); if (op == MIPS_MAKE_JR_RA()) { - WriteReplaceInstruction(address, index); + WriteReplaceInstruction(address + offset, index); } } } else if (entry->flags & REPFLAG_HOOKENTER) { diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index dd67a452c6..be49300bb6 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -347,6 +347,7 @@ static const HardHashTableEntry hardcodedHashes[] = { { 0xc51519f5dab342d4, 224, "cosf", }, { 0xc52c14b9af8c3008, 76, "memcmp", }, { 0xc54eae62622f1e11, 164, "dl_write_bone_matrix_2", }, + { 0xc6b29de7d3245198, 656, "starocean_write_stencil" }, // Star Ocean 1 { 0xc96e3a087ebf49a9, 100, "dl_write_light_color", }, { 0xcb7a2edd603ecfef, 48, "vtfm_p", }, { 0xcdf64d21418b2667, 24, "vzero_q", }, diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index f11335a76d..ac7a596874 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -1330,6 +1330,10 @@ bool DIRECTX9_GPU::PerformMemoryDownload(u32 dest, int size) { return false; } +bool DIRECTX9_GPU::PerformStencilUpload(u32 dest, int size) { + return false; +} + void DIRECTX9_GPU::ClearCacheNextFrame() { textureCache_.ClearNextFrame(); } diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index a0124529af..35ff0dc4ba 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -49,6 +49,7 @@ public: virtual bool PerformMemoryCopy(u32 dest, u32 src, int size); virtual bool PerformMemorySet(u32 dest, u8 v, int size); virtual bool PerformMemoryDownload(u32 dest, int size); + virtual bool PerformStencilUpload(u32 dest, int size); virtual void ClearCacheNextFrame(); virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders. diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index c78a846282..889dd637ce 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -101,7 +101,7 @@ enum { FBO_OLD_AGE = 5, }; -static bool MaskedEqual(u32 addr1, u32 addr2) { +bool FramebufferManager::MaskedEqual(u32 addr1, u32 addr2) { return (addr1 & 0x03FFFFFF) == (addr2 & 0x03FFFFFF); } @@ -160,7 +160,7 @@ void CenterRect(float *x, float *y, float *w, float *h, *h = outH; } -static void ClearBuffer() { +void FramebufferManager::ClearBuffer() { glstate.scissorTest.disable(); glstate.depthWrite.set(GL_TRUE); glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -175,7 +175,7 @@ static void ClearBuffer() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } -static void DisableState() { +void FramebufferManager::DisableState() { glstate.blend.disable(); glstate.cullFace.disable(); glstate.depthTest.disable(); @@ -322,6 +322,7 @@ FramebufferManager::FramebufferManager() : convBuf_(0), draw2dprogram_(0), postShaderProgram_(0), + stencilUploadProgram_(0), plainColorLoc_(-1), timeLoc_(-1), textureCache_(0), @@ -361,6 +362,9 @@ FramebufferManager::~FramebufferManager() { if (draw2dprogram_) { glsl_destroy(draw2dprogram_); } + if (stencilUploadProgram_) { + glsl_destroy(stencilUploadProgram_); + } SetNumExtraFBOs(0); for (auto it = tempFBOs_.begin(), end = tempFBOs_.end(); it != end; ++it) { @@ -418,9 +422,9 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr for (int x = 0; x < width; x++) { u16 col = src[x]; - dst[x * 4] = ((col) & 0x1f) << 3; - dst[x * 4 + 1] = ((col >> 5) & 0x3f) << 2; - dst[x * 4 + 2] = ((col >> 11) & 0x1f) << 3; + dst[x * 4] = Convert5To8((col) & 0x1f); + dst[x * 4 + 1] = Convert6To8((col >> 5) & 0x3f); + dst[x * 4 + 2] = Convert5To8((col >> 11) & 0x1f); dst[x * 4 + 3] = 255; } } @@ -433,9 +437,9 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr for (int x = 0; x < width; x++) { u16 col = src[x]; - dst[x * 4] = ((col) & 0x1f) << 3; - dst[x * 4 + 1] = ((col >> 5) & 0x1f) << 3; - dst[x * 4 + 2] = ((col >> 10) & 0x1f) << 3; + dst[x * 4] = Convert5To8((col) & 0x1f); + dst[x * 4 + 1] = Convert5To8((col >> 5) & 0x1f); + dst[x * 4 + 2] = Convert5To8((col >> 10) & 0x1f); dst[x * 4 + 3] = (col >> 15) ? 255 : 0; } } @@ -448,10 +452,10 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr for (int x = 0; x < width; x++) { u16 col = src[x]; - dst[x * 4] = ((col >> 8) & 0xf) << 4; - dst[x * 4 + 1] = ((col >> 4) & 0xf) << 4; - dst[x * 4 + 2] = (col & 0xf) << 4; - dst[x * 4 + 3] = (col >> 12) << 4; + dst[x * 4] = Convert4To8((col >> 8) & 0xf); + dst[x * 4 + 1] = Convert4To8((col >> 4) & 0xf); + dst[x * 4 + 2] = Convert4To8(col & 0xf); + dst[x * 4 + 3] = Convert4To8(col >> 12); } } break; diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 8137c39cf3..7dd127b1ff 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -212,6 +212,7 @@ public: inline bool ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const; bool NotifyFramebufferCopy(u32 src, u32 dest, int size, bool isMemset = false); + bool NotifyStencilUpload(u32 addr, int size); void DestroyFramebuf(VirtualFramebuffer *vfb); @@ -232,6 +233,10 @@ private: void SetNumExtraFBOs(int num); + static void DisableState(); + static void ClearBuffer(); + static bool MaskedEqual(u32 addr1, u32 addr2); + u32 displayFramebufPtr_; u32 displayStride_; GEBufferFormat displayFormat_; @@ -263,6 +268,7 @@ private: GLSLProgram *draw2dprogram_; GLSLProgram *plainColorProgram_; GLSLProgram *postShaderProgram_; + GLSLProgram *stencilUploadProgram_; int plainColorLoc_; int timeLoc_; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 3ccd375063..0b9b0b57bb 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -674,6 +674,10 @@ void GLES_GPU::ProcessEvent(GPUEvent ev) { PerformMemorySetInternal(ev.fb_memset.dst, ev.fb_memset.v, ev.fb_memset.size); break; + case GPU_EVENT_FB_STENCIL_UPLOAD: + PerformStencilUploadInternal(ev.fb_stencil_upload.dst, ev.fb_stencil_upload.size); + break; + default: GPUCommon::ProcessEvent(ev); } @@ -2004,6 +2008,10 @@ void GLES_GPU::PerformMemorySetInternal(u32 dest, u8 v, int size) { } } +void GLES_GPU::PerformStencilUploadInternal(u32 dest, int size) { + framebufferManager_.NotifyStencilUpload(dest, size); +} + bool GLES_GPU::PerformMemoryCopy(u32 dest, u32 src, int size) { // Track stray copies of a framebuffer in RAM. MotoGP does this. if (framebufferManager_.MayIntersectFramebuffer(src) || framebufferManager_.MayIntersectFramebuffer(dest)) { @@ -2056,6 +2064,21 @@ bool GLES_GPU::PerformMemoryDownload(u32 dest, int size) { return gpu->PerformMemoryCopy(dest ^ 0x00400000, dest, size); } +bool GLES_GPU::PerformStencilUpload(u32 dest, int size) { + if (framebufferManager_.MayIntersectFramebuffer(dest)) { + if (IsOnSeparateCPUThread()) { + GPUEvent ev(GPU_EVENT_FB_STENCIL_UPLOAD); + ev.fb_stencil_upload.dst = dest; + ev.fb_stencil_upload.size = size; + ScheduleEvent(ev); + } else { + PerformStencilUploadInternal(dest, size); + } + return true; + } + return false; +} + void GLES_GPU::ClearCacheNextFrame() { textureCache_.ClearNextFrame(); } diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index f60bdd73d6..85bcb032c1 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -48,6 +48,7 @@ public: virtual bool PerformMemoryCopy(u32 dest, u32 src, int size); virtual bool PerformMemorySet(u32 dest, u8 v, int size); virtual bool PerformMemoryDownload(u32 dest, int size); + virtual bool PerformStencilUpload(u32 dest, int size); virtual void ClearCacheNextFrame(); virtual void DeviceLost(); // Only happens on Android. Drop all textures and shaders. @@ -158,6 +159,7 @@ private: void CopyDisplayToOutputInternal(); void PerformMemoryCopyInternal(u32 dest, u32 src, int size); void PerformMemorySetInternal(u32 dest, u8 v, int size); + void PerformStencilUploadInternal(u32 dest, int size); void InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType type); static CommandInfo cmdInfo_[256]; diff --git a/GPU/GLES/StencilBuffer.cpp b/GPU/GLES/StencilBuffer.cpp new file mode 100644 index 0000000000..69a144172c --- /dev/null +++ b/GPU/GLES/StencilBuffer.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2014- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "gfx_es2/glsl_program.h" +#include "gfx_es2/gl_state.h" +#include "Core/Reporting.h" +#include "GPU/GLES/Framebuffer.h" +#include "GPU/GLES/ShaderManager.h" + +static const char *stencil_fs = +#ifdef USING_GLES +"#version 100\n" +"precision highp float;\n" +#endif +"varying vec2 v_texcoord0;\n" +"uniform float u_stencilValue;\n" +"uniform sampler2D tex;\n" +"float roundAndScaleTo255f(in float x) { return floor(x * 255.99); }\n" +"void main() {\n" +" vec4 index = texture2D(tex, v_texcoord0);\n" +" gl_FragColor = vec4(1.0);\n" +" float shifted = roundAndScaleTo255f(index.a) / roundAndScaleTo255f(u_stencilValue);\n" +" if (mod(floor(shifted), 2.0) < 0.99) discard;\n" +"}\n"; + +static const char *stencil_vs = +#ifdef USING_GLES +"#version 100\n" +"precision highp float;\n" +#endif +"attribute vec4 a_position;\n" +"attribute vec2 a_texcoord0;\n" +"varying vec2 v_texcoord0;\n" +"void main() {\n" +" v_texcoord0 = a_texcoord0;\n" +" gl_Position = a_position;\n" +"}\n"; + +static bool MaskedEqual(u32 addr1, u32 addr2) { + return (addr1 & 0x03FFFFFF) == (addr2 & 0x03FFFFFF); +} + +bool FramebufferManager::NotifyStencilUpload(u32 addr, int size) { + if (!MayIntersectFramebuffer(addr)) { + return false; + } + + VirtualFramebuffer *dstBuffer = 0; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + if (MaskedEqual(vfb->fb_address, addr)) { + dstBuffer = vfb; + } + } + if (!dstBuffer) { + return false; + } + + GLSLProgram *program = 0; + if (!stencilUploadProgram_) { + std::string errorString; + stencilUploadProgram_ = glsl_create_source(stencil_vs, stencil_fs, &errorString); + if (!stencilUploadProgram_) { + ERROR_LOG_REPORT(G3D, "Failed to compile stencilUploadProgram! This shouldn't happen.\n%s", errorString.c_str()); + } else { + glsl_bind(stencilUploadProgram_); + } + + GLint u_tex = glsl_uniform_loc(stencilUploadProgram_, "tex"); + glUniform1i(u_tex, 0); + } else { + glsl_bind(stencilUploadProgram_); + } + + shaderManager_->DirtyLastShader(); + + MakePixelTexture(Memory::GetPointer(addr), dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height); + DisableState(); + glstate.blend.set(true); + glstate.blendEquation.set(GL_FUNC_ADD); + glstate.blendFuncSeparate.set(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO); + glstate.stencilTest.enable(); + glstate.stencilOp.set(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + // TODO: Doing it the slow way for now. + int values = 0; + + switch (dstBuffer->format) { + case GE_FORMAT_565: + // Well, this doesn't make much sense. + return false; + case GE_FORMAT_5551: + values = 2; + break; + case GE_FORMAT_4444: + values = 16; + break; + case GE_FORMAT_8888: + values = 256; + break; + } + + if (dstBuffer->fbo) { + fbo_bind_as_render_target(dstBuffer->fbo); + } + glViewport(0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight); + + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + glstate.stencilFunc.set(GL_ALWAYS, 0xFF, 0xFF); + + GLint u_stencilValue = glsl_uniform_loc(stencilUploadProgram_, "u_stencilValue"); + for (int i = 1; i < values; i += i) { + // DrawActiveTexture unbinds it, so rebind here before setting uniforms. + glsl_bind(stencilUploadProgram_); + if (dstBuffer->format == GE_FORMAT_4444) { + glStencilMask(Convert4To8(i)); + glUniform1f(u_stencilValue, i * (16.0f / 255.0f)); + } else if (dstBuffer->format == GE_FORMAT_5551) { + glStencilMask(0xFF); + glUniform1f(u_stencilValue, i * (128.0f / 255.0f)); + } else { + glStencilMask(i); + glUniform1f(u_stencilValue, i * (1.0f / 255.0f)); + } + DrawActiveTexture(0, 0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->width, dstBuffer->height, false, 0.0f, 0.0f, 1.0f, 1.0f, stencilUploadProgram_); + } + glStencilMask(0xFF); + + if (currentRenderVfb_) { + RebindFramebuffer(); + } else { + fbo_unbind(); + } + glstate.viewport.restore(); + return true; +} \ No newline at end of file diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 679f65db58..cbea9ef634 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -250,6 +250,7 @@ + @@ -285,4 +286,4 @@ - + \ No newline at end of file diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index b420625616..e3ba8997c2 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -314,8 +314,11 @@ GLES + + GLES + - + \ No newline at end of file diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 9b29072c73..e8f6844514 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -165,6 +165,7 @@ enum GPUEventType { GPU_EVENT_SYNC_THREAD, GPU_EVENT_FB_MEMCPY, GPU_EVENT_FB_MEMSET, + GPU_EVENT_FB_STENCIL_UPLOAD, }; struct GPUEvent { @@ -189,6 +190,11 @@ struct GPUEvent { u8 v; int size; } fb_memset; + // GPU_EVENT_FB_STENCIL_UPLOAD + struct { + u32 dst; + int size; + } fb_stencil_upload; }; operator GPUEventType() const { @@ -244,6 +250,7 @@ public: virtual bool PerformMemoryCopy(u32 dest, u32 src, int size) = 0; virtual bool PerformMemorySet(u32 dest, u8 v, int size) = 0; virtual bool PerformMemoryDownload(u32 dest, int size) = 0; + virtual bool PerformStencilUpload(u32 dest, int size) = 0; // Will cause the texture cache to be cleared at the start of the next frame. virtual void ClearCacheNextFrame() = 0; diff --git a/GPU/Null/NullGpu.cpp b/GPU/Null/NullGpu.cpp index d718cb4059..f427b909d0 100644 --- a/GPU/Null/NullGpu.cpp +++ b/GPU/Null/NullGpu.cpp @@ -674,3 +674,7 @@ bool NullGPU::PerformMemoryDownload(u32 dest, int size) { InvalidateCache(dest, size, GPU_INVALIDATE_HINT); return false; } + +bool NullGPU::PerformStencilUpload(u32 dest, int size) { + return false; +} diff --git a/GPU/Null/NullGpu.h b/GPU/Null/NullGpu.h index 724403be5a..a83b256e1e 100644 --- a/GPU/Null/NullGpu.h +++ b/GPU/Null/NullGpu.h @@ -37,6 +37,7 @@ public: virtual bool PerformMemoryCopy(u32 dest, u32 src, int size); virtual bool PerformMemorySet(u32 dest, u8 v, int size); virtual bool PerformMemoryDownload(u32 dest, int size); + virtual bool PerformStencilUpload(u32 dest, int size); virtual void ClearCacheNextFrame() {}; virtual void DeviceLost() {} diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 20db506252..2d3e38c522 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -876,6 +876,11 @@ bool SoftGPU::PerformMemoryDownload(u32 dest, int size) return false; } +bool SoftGPU::PerformStencilUpload(u32 dest, int size) +{ + return false; +} + bool SoftGPU::FramebufferDirty() { if (g_Config.bSeparateCPUThread) { // Allow it to process fully before deciding if it's dirty. diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 8144f870ee..9ffe4509bd 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -62,6 +62,7 @@ public: virtual bool PerformMemoryCopy(u32 dest, u32 src, int size); virtual bool PerformMemorySet(u32 dest, u8 v, int size); virtual bool PerformMemoryDownload(u32 dest, int size); + virtual bool PerformStencilUpload(u32 dest, int size); virtual void ClearCacheNextFrame() {}; virtual void DeviceLost() {} diff --git a/Qt/Core.pro b/Qt/Core.pro index 5d0bd6befb..4300756fa7 100755 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -54,6 +54,7 @@ SOURCES += $$P/Core/*.cpp \ # Core $$P/GPU/GLES/SoftwareTransform.cpp \ $$P/GPU/GLES/Spline.cpp \ $$P/GPU/GLES/StateMapping.cpp \ + $$P/GPU/GLES/StencilBuffer.cpp \ $$P/GPU/GLES/TextureCache.cpp \ $$P/GPU/GLES/TextureScaler.cpp \ $$P/GPU/GLES/TransformPipeline.cpp \ diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 8bd8a9abaa..f4b71ccf3f 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -146,6 +146,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/GLES/Framebuffer.cpp \ $(SRC)/GPU/GLES/DepalettizeShader.cpp \ $(SRC)/GPU/GLES/GLES_GPU.cpp.arm \ + $(SRC)/GPU/GLES/StencilBuffer.cpp.arm \ $(SRC)/GPU/GLES/TextureCache.cpp.arm \ $(SRC)/GPU/GLES/TransformPipeline.cpp.arm \ $(SRC)/GPU/GLES/SoftwareTransform.cpp.arm \