diff --git a/game/graphics/opengl_renderer/DirectRenderer.cpp b/game/graphics/opengl_renderer/DirectRenderer.cpp index 6f512453f7..8fec0fed4a 100644 --- a/game/graphics/opengl_renderer/DirectRenderer.cpp +++ b/game/graphics/opengl_renderer/DirectRenderer.cpp @@ -94,7 +94,6 @@ void DirectRenderer::render(DmaFollower& dma, pre_render(); // if we're rendering from a bucket, we should start off we a totally reset state: reset_state(); - setup_common_state(render_state); // just dump the DMA data into the other the render function while (dma.current_tag_offset() != render_state->next_bucket) { @@ -562,10 +561,6 @@ void DirectRenderer::update_gl_test() { } } -void DirectRenderer::setup_common_state(SharedRenderState* /*render_state*/) { - // todo texture clamp. -} - namespace { /*! * If it's a direct, returns the qwc. diff --git a/game/graphics/opengl_renderer/DirectRenderer.h b/game/graphics/opengl_renderer/DirectRenderer.h index 0d347644b3..5ea1288017 100644 --- a/game/graphics/opengl_renderer/DirectRenderer.h +++ b/game/graphics/opengl_renderer/DirectRenderer.h @@ -50,11 +50,6 @@ class DirectRenderer : public BucketRenderer { void reset_state(); - /*! - * If you don't use the render interface, call this first to set up OpenGL. - */ - void setup_common_state(SharedRenderState* render_state); - /*! * If you don't use the render interface, call this at the very end. */ diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index 468e65c778..9a7fdcaea3 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -292,8 +292,8 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { 0x1000); init_bucket_renderer("screen-filter", BucketCategory::OTHER, BucketId::SCREEN_FILTER, 256); - init_bucket_renderer("subtitle", BucketCategory::OTHER, BucketId::SUBTITLE, - 0x1000); + init_bucket_renderer("subtitle", BucketCategory::OTHER, BucketId::SUBTITLE, + m_texture_animator, true); init_bucket_renderer("debug2", BucketCategory::OTHER, BucketId::DEBUG2, 0x8000); init_bucket_renderer("debug-no-zbuf2", BucketCategory::OTHER, BucketId::DEBUG_NO_ZBUF2, 0x8000); diff --git a/game/graphics/opengl_renderer/TextureAnimator.cpp b/game/graphics/opengl_renderer/TextureAnimator.cpp index 434620f21c..520a101f51 100644 --- a/game/graphics/opengl_renderer/TextureAnimator.cpp +++ b/game/graphics/opengl_renderer/TextureAnimator.cpp @@ -96,9 +96,20 @@ OpenGLTexturePool::~OpenGLTexturePool() { * Get a preallocated texture with the given size, or fatal error if we are out. */ GLuint OpenGLTexturePool::allocate(u64 w, u64 h) { - const auto& it = textures.find((w << 32) | h); + const u64 key = (w << 32) | h; + const auto& it = textures.find(key); if (it == textures.end()) { - lg::die("OpenGLTexturePool needs entries for {} x {}", w, h); + // Note: this is a bit of an abuse to support both Japanese subtitles (variable size), and the + // "emulated" cloud textures (preallocated to avoid the performance issue described at the top + // of the file). For now, warn when this happens, just so we don't miss a case of this getting + // spammed during normal gameplay (bad for performance). Note that all of this can get massively + // simplified once clouds are moved to C++. This is just a hack to keep the current clouds + // working. (they are wrong and slow, but look better than nothing) + lg::warn("OpenGLTexturePool creating texture for {} x {}", w, h); + GLuint slot; + glGenTextures(1, &slot); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); + return slot; } if (it->second.empty()) { @@ -858,6 +869,32 @@ void TextureAnimator::force_to_gpu(int tbp) { glBindTexture(GL_TEXTURE_2D, 0); entry.kind = VramEntry::Kind::GPU; } break; + case VramEntry::Kind::GENERIC_PSMT4: { + int tw = entry.tex_width; + int th = entry.tex_height; + std::vector rgba_data(tw * th); + { + auto p = scoped_prof("convert"); + // for psmt4, we don't use the special 16x16 case + const auto& clut_lookup = m_textures.find(entry.cbp); + ASSERT(clut_lookup != m_textures.end()); + ASSERT(clut_lookup->second.kind == VramEntry::Kind::GENERIC_PSM32); + auto* clut = (const u32*)clut_lookup->second.data.data(); + + for (int px = 0; px < (int)rgba_data.size(); ++px) { + u8 val = entry.data[px / 2]; + int idx = px & 1 ? val >> 4 : val & 0xf; + // no m_index_to_clut_addr mapping for the 4-bit index. + rgba_data[px] = clut[idx]; + } + } + setup_vram_entry_for_gpu_texture(tw, th, tbp); + glBindTexture(GL_TEXTURE_2D, entry.tex.value().texture()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, + rgba_data.data()); + glBindTexture(GL_TEXTURE_2D, 0); + entry.kind = VramEntry::Kind::GPU; + } break; } } @@ -1084,6 +1121,9 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_ vram.tex_width = upload->width; vram.tex_height = upload->height; memcpy(vram.data.data(), ee_mem + upload->data, vram.data.size()); + if (m_tex_looking_for_clut) { + m_tex_looking_for_clut->cbp = upload->dest; + } m_tex_looking_for_clut = nullptr; if (upload->force_to_gpu) { m_erased_on_this_frame.insert(upload->dest); @@ -1100,6 +1140,17 @@ void TextureAnimator::handle_generic_upload(const DmaTransfer& tf, const u8* ee_ m_erased_on_this_frame.insert(upload->dest); } break; + case (int)GsTex0::PSM::PSMT4: + vram.kind = VramEntry::Kind::GENERIC_PSMT4; + vram.data.resize(upload->width * upload->height); + vram.tex_width = upload->width; + vram.tex_height = upload->height; + memcpy(vram.data.data(), ee_mem + upload->data, vram.data.size()); + m_tex_looking_for_clut = &vram; + if (upload->force_to_gpu) { + m_erased_on_this_frame.insert(upload->dest); + } + break; default: fmt::print("Unhandled format: {}\n", upload->format); ASSERT_NOT_REACHED(); diff --git a/game/graphics/opengl_renderer/TextureAnimator.h b/game/graphics/opengl_renderer/TextureAnimator.h index fc04e0100c..7bd9719826 100644 --- a/game/graphics/opengl_renderer/TextureAnimator.h +++ b/game/graphics/opengl_renderer/TextureAnimator.h @@ -20,7 +20,14 @@ struct GpuTexture; struct VramEntry { - enum class Kind { CLUT16_16_IN_PSM32, GENERIC_PSM32, GENERIC_PSMT8, GPU, INVALID } kind; + enum class Kind { + CLUT16_16_IN_PSM32, + GENERIC_PSM32, + GENERIC_PSMT8, + GENERIC_PSMT4, + GPU, + INVALID + } kind; std::vector data; int tex_width = 0; diff --git a/game/graphics/opengl_renderer/TextureUploadHandler.cpp b/game/graphics/opengl_renderer/TextureUploadHandler.cpp index 2d6729e2e7..32ca9e0eaf 100644 --- a/game/graphics/opengl_renderer/TextureUploadHandler.cpp +++ b/game/graphics/opengl_renderer/TextureUploadHandler.cpp @@ -26,6 +26,9 @@ void TextureUploadHandler::render(DmaFollower& dma, // this is the data we get from the PC Port modification. m_upload_count = 0; std::vector uploads; + if (m_direct) { + m_direct->reset_state(); + } // loop through all data, grabbing buckets while (dma.current_tag_offset() != render_state->next_bucket) { auto dma_tag = dma.current_tag(); diff --git a/goal_src/jak2/engine/scene/scene.gc b/goal_src/jak2/engine/scene/scene.gc index ac0b3dc96c..b2371a1251 100644 --- a/goal_src/jak2/engine/scene/scene.gc +++ b/goal_src/jak2/engine/scene/scene.gc @@ -584,6 +584,33 @@ ) ) +(defun pc-upload-subtitle-texture ((dma-buf dma-buffer) (image-data pointer) (clut-data pointer) (width int) (height int) (tbp int) (cbp int)) + "Added PC-port function to send a mt4 texture." + (pc-texture-anim-flag start-anim-array dma-buf) + (pc-texture-anim-flag upload-generic-vram dma-buf :qwc 1) + (let ((upload-record (the texture-anim-pc-upload (-> dma-buf base)))) + (set! (-> upload-record data) clut-data) + (set! (-> upload-record width) 2) + (set! (-> upload-record height) 8) + (set! (-> upload-record dest) cbp) + (set! (-> upload-record format) (gs-psm ct32)) + (set! (-> upload-record force-to-gpu) 0) + ) + (&+! (-> dma-buf base) 16) + + (pc-texture-anim-flag upload-generic-vram dma-buf :qwc 1) + (let ((upload-record (the texture-anim-pc-upload (-> dma-buf base)))) + (set! (-> upload-record data) image-data) + (set! (-> upload-record width) width) + (set! (-> upload-record height) height) + (set! (-> upload-record dest) tbp) + (set! (-> upload-record format) (gs-psm mt4)) + (set! (-> upload-record force-to-gpu) 1) + ) + (&+! (-> dma-buf base) 16) + (pc-texture-anim-flag finish-anim-array dma-buf) + ) + ;; WARN: Return type mismatch pointer vs none. (defun draw-subtitle-image ((arg0 subtitle-image) (arg1 font-context)) (local-vars (sv-16 pointer) (sv-32 int)) @@ -598,29 +625,39 @@ (with-dma-buffer-add-bucket ((s3-0 (-> *display* frames (-> *display* on-screen) global-buf)) (bucket-id subtitle) ) - (upload-vram-data s3-0 0 (-> arg0 palette) 2 8) + ;; (upload-vram-data s3-0 0 (-> arg0 palette) 2 8) (let ((s0-0 20)) - (dma-buffer-add-gs-set s3-0 - (bitbltbuf (new 'static 'gs-bitbltbuf :dbp #x1 :dbw (shr gp-0 6) :dpsm s0-0)) - (trxpos (new 'static 'gs-trxpos)) - (trxreg (new 'static 'gs-trxreg :rrw gp-0 :rrh s5-0)) - (trxdir (new 'static 'gs-trxdir)) - ) - (let ((t9-2 dma-buffer-add-ref-texture) - (a0-13 s3-0) - (a2-8 gp-0) - (a3-1 s5-0) - (t0-1 s0-0) - ) - (t9-2 a0-13 sv-16 (the-as int a2-8) (the-as int a3-1) (the-as gs-psm t0-1)) + (pc-upload-subtitle-texture + s3-0 ;; dma-buf + sv-16 ;; image data + (-> arg0 palette) ;; clut data + (the int (-> arg0 width)) + (the int (-> arg0 height)) + 1 ;; tbp + 0 ;; cbp ) + ; (dma-buffer-add-gs-set s3-0 + ; (bitbltbuf (new 'static 'gs-bitbltbuf :dbp #x1 :dbw (shr gp-0 6) :dpsm s0-0)) + ; (trxpos (new 'static 'gs-trxpos)) + ; (trxreg (new 'static 'gs-trxreg :rrw gp-0 :rrh s5-0)) + ; (trxdir (new 'static 'gs-trxdir)) + ; ) + ; (let ((t9-2 dma-buffer-add-ref-texture) + ; (a0-13 s3-0) + ; (a2-8 gp-0) + ; (a3-1 s5-0) + ; (t0-1 s0-0) + ; ) + ; (t9-2 a0-13 sv-16 (the-as int a2-8) (the-as int a3-1) (the-as gs-psm t0-1)) + ; ) (set! sv-32 (+ (log2 (the-as int (+ gp-0 -1))) 1)) (let ((v1-17 (+ (log2 (the-as int (+ s5-0 -1))) 1))) (dma-buffer-add-gs-set s3-0 (test-1 (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))) (alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1)) (tex0-1 (new 'static 'gs-tex0 :tbp0 #x1 :tcc #x1 :cld #x1 :psm s0-0 :th v1-17 :tw sv-32 :tbw (shr gp-0 6))) - (tex1-1 (new 'static 'gs-tex1)) + ;; added texture filtering for PC-port here: + (tex1-1 (new 'static 'gs-tex1 :mmag 1 :mmin 1)) (clamp-1 (new 'static 'gs-clamp :wms (gs-tex-wrap-mode clamp) :wmt (gs-tex-wrap-mode clamp))) (texflush 0) ) @@ -737,10 +774,9 @@ (new 'static 'sound-id) ) ) - ;; TODO - crashes the game - ;; ((subtitle-image) - ;; (draw-subtitle-image (the-as subtitle-image s3-0) s2-0) - ;; ) + ((subtitle-image) + (draw-subtitle-image (the-as subtitle-image s3-0) s2-0) + ) (else (if *debug-segment* (format *stdcon* "unknown message ~A~%" s3-0)