diff --git a/UnleashedRecomp/gpu/shader/csd_no_tex_vs.hlsl b/UnleashedRecomp/gpu/shader/csd_no_tex_vs.hlsl index 48ec6af1..1654e734 100644 --- a/UnleashedRecomp/gpu/shader/csd_no_tex_vs.hlsl +++ b/UnleashedRecomp/gpu/shader/csd_no_tex_vs.hlsl @@ -43,7 +43,7 @@ void main( out float4 oColor0 : COLOR0, out float4 oColor1 : COLOR1) { - oPos.xy = (iPosition0.xy - 0.5) * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); + oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); oPos.z = g_Z.x; oPos.w = 1.0; oTexCoord0 = iColor0.wxyz; diff --git a/UnleashedRecomp/gpu/shader/csd_vs.hlsl b/UnleashedRecomp/gpu/shader/csd_vs.hlsl index 11463684..90b561f5 100644 --- a/UnleashedRecomp/gpu/shader/csd_vs.hlsl +++ b/UnleashedRecomp/gpu/shader/csd_vs.hlsl @@ -44,7 +44,7 @@ void main( out float4 oColor0 : COLOR0, out float4 oColor1 : COLOR1) { - oPos.xy = (iPosition0.xy - 0.5) * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); + oPos.xy = iPosition0.xy * g_ViewportSize.zw * float2(2.0, -2.0) + float2(-1.0, 1.0); oPos.z = g_Z.x; oPos.w = 1.0; oTexCoord0 = iColor0.wxyz; diff --git a/UnleashedRecomp/gpu/shader/movie_vs.hlsl b/UnleashedRecomp/gpu/shader/movie_vs.hlsl index c4e47c1c..6d00919e 100644 --- a/UnleashedRecomp/gpu/shader/movie_vs.hlsl +++ b/UnleashedRecomp/gpu/shader/movie_vs.hlsl @@ -4,6 +4,7 @@ Interpolators main(in VertexShaderInput In) { Interpolators Out; Out.ProjPos = In.ObjPos; + Out.ProjPos.xy += g_HalfPixelOffset * Out.ProjPos.w; Out.UV = In.UV; return Out; } diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index e24ce4de..42b39217 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -163,6 +163,8 @@ struct SharedConstants uint32_t samplerIndices[16]{}; uint32_t booleans{}; uint32_t swappedTexcoords{}; + float halfPixelOffsetX{}; + float halfPixelOffsetY{}; float alphaThreshold{}; }; @@ -679,7 +681,10 @@ static void DestructTempResources() g_textureDescriptorAllocator.free(texture->descriptorIndex); if (texture->patchedTexture != nullptr) - g_textureDescriptorAllocator.free(texture->patchedTexture->descriptorIndex); + g_textureDescriptorAllocator.free(texture->patchedTexture->descriptorIndex); + + if (texture->recreatedCubeMapTexture != nullptr) + g_textureDescriptorAllocator.free(texture->recreatedCubeMapTexture->descriptorIndex); texture->~GuestTexture(); break; @@ -3089,8 +3094,6 @@ static void FlushViewport() if (g_dirtyStates.viewport) { auto viewport = g_viewport; - viewport.x += 0.5f; - viewport.y += 0.5f; if (viewport.minDepth > viewport.maxDepth) std::swap(viewport.minDepth, viewport.maxDepth); @@ -3514,6 +3517,12 @@ static void SetFramebuffer(GuestSurface* renderTarget, GuestSurface* depthStenci g_framebuffer = nullptr; } + if (g_framebuffer != nullptr) + { + SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.halfPixelOffsetX, 1.0f / float(g_framebuffer->getWidth())); + SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.halfPixelOffsetY, -1.0f / float(g_framebuffer->getHeight())); + } + g_dirtyStates.renderTargetAndDepthStencil = settingForClear; } } @@ -3641,6 +3650,14 @@ static void SetTextureInRenderThread(uint32_t index, GuestTexture* texture) SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.texture3DIndices[index], texture != nullptr && viewDimension == RenderTextureViewDimension::TEXTURE_3D ? texture->descriptorIndex : TEXTURE_DESCRIPTOR_NULL_TEXTURE_3D); + // Check if there's a cubemap texture we recreated and assign it if it's valid. The shader will pick whichever is correct. + if (viewDimension == RenderTextureViewDimension::TEXTURE_2D && texture->recreatedCubeMapTexture != nullptr) + { + texture = texture->recreatedCubeMapTexture.get(); + AddBarrier(texture, RenderTextureLayout::SHADER_READ); + viewDimension = texture->viewDimension; + } + SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.textureCubeIndices[index], texture != nullptr && viewDimension == RenderTextureViewDimension::TEXTURE_CUBE ? texture->descriptorIndex : TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE); } @@ -5471,21 +5488,30 @@ static RenderFormat ConvertDXGIFormat(ddspp::DXGIFormat format) } } -static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping) +static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping, bool forceCubeMap = false) { ddspp::Descriptor ddsDesc; if (ddspp::decode_header((unsigned char *)(data), ddsDesc) != ddspp::Error) { + forceCubeMap &= (ddsDesc.type == ddspp::Texture2D) && (ddsDesc.arraySize == 1); + uint32_t arraySize = ddsDesc.type == ddspp::TextureType::Cubemap ? (ddsDesc.arraySize * 6) : ddsDesc.arraySize; + RenderTextureDesc desc; desc.dimension = ConvertTextureDimension(ddsDesc.type); desc.width = ddsDesc.width; desc.height = ddsDesc.height; desc.depth = ddsDesc.depth; desc.mipLevels = ddsDesc.numMips; - desc.arraySize = ddsDesc.type == ddspp::TextureType::Cubemap ? ddsDesc.arraySize * 6 : ddsDesc.arraySize; + desc.arraySize = arraySize; desc.format = ConvertDXGIFormat(ddsDesc.format); desc.flags = ddsDesc.type == ddspp::TextureType::Cubemap ? RenderTextureFlag::CUBE : RenderTextureFlag::NONE; + if (forceCubeMap) + { + desc.arraySize = 6; + desc.flags = RenderTextureFlag::CUBE; + } + texture.textureHolder = g_device->createTexture(desc); texture.texture = texture.textureHolder.get(); texture.layout = RenderTextureLayout::COPY_DEST; @@ -5495,6 +5521,10 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS viewDesc.dimension = ConvertTextureViewDimension(ddsDesc.type); viewDesc.mipLevels = ddsDesc.numMips; viewDesc.componentMapping = componentMapping; + + if (forceCubeMap) + viewDesc.dimension = RenderTextureViewDimension::TEXTURE_CUBE; + texture.textureView = texture.texture->createTextureView(viewDesc); texture.descriptorIndex = g_textureDescriptorAllocator.allocate(); g_textureDescriptorSet->setTexture(texture.descriptorIndex, texture.texture, RenderTextureLayout::SHADER_READ, texture.textureView.get()); @@ -5519,7 +5549,7 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS uint32_t curSrcOffset = 0; uint32_t curDstOffset = 0; - for (uint32_t arraySlice = 0; arraySlice < desc.arraySize; arraySlice++) + for (uint32_t arraySlice = 0; arraySlice < arraySize; arraySlice++) { for (uint32_t mipSlice = 0; mipSlice < ddsDesc.numMips; mipSlice++) { @@ -5569,13 +5599,24 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS { g_copyCommandList->barriers(RenderBarrierStage::COPY, RenderTextureBarrier(texture.texture, RenderTextureLayout::COPY_DEST)); - for (size_t i = 0; i < slices.size(); i++) - { - auto& slice = slices[i]; + auto copyTextureRegion = [&](Slice& slice, uint32_t subresourceIndex) + { + g_copyCommandList->copyTextureRegion( + RenderTextureCopyLocation::Subresource(texture.texture, subresourceIndex), + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), desc.format, slice.width, slice.height, slice.depth, (slice.dstRowPitch * 8) / ddsDesc.bitsPerPixelOrBlock * ddsDesc.blockWidth, slice.dstOffset)); + }; - g_copyCommandList->copyTextureRegion( - RenderTextureCopyLocation::Subresource(texture.texture, i), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), desc.format, slice.width, slice.height, slice.depth, (slice.dstRowPitch * 8) / ddsDesc.bitsPerPixelOrBlock * ddsDesc.blockWidth, slice.dstOffset)); + for (size_t i = 0; i < slices.size(); i++) + copyTextureRegion(slices[i], i); + + // Duplicate the first face across the remaining 6 faces. + if (forceCubeMap) + { + for (size_t i = 1; i < 6; i++) + { + for (size_t j = 0; j < slices.size(); j++) + copyTextureRegion(slices[j], (slices.size() * i) + j); + } } }); @@ -5648,13 +5689,12 @@ std::unique_ptr LoadTexture(const uint8_t* data, size_t dataSize, return nullptr; } -static void DiffPatchTexture(GuestTexture& texture, uint8_t* data, uint32_t dataSize) +static void DiffPatchTexture(GuestTexture& texture, uint8_t* data, uint32_t dataSize, XXH64_hash_t hash) { auto header = reinterpret_cast(g_buttonBcDiff.get()); auto entries = reinterpret_cast(g_buttonBcDiff.get() + header->entriesOffset); auto end = entries + header->entryCount; - XXH64_hash_t hash = XXH3_64bits(data, dataSize); auto findResult = std::lower_bound(entries, end, hash, [](BlockCompressionDiffPatchEntry& lhs, XXH64_hash_t rhs) { return lhs.hash < rhs; @@ -5687,8 +5727,19 @@ static void MakePictureData(GuestPictureData* pictureData, uint8_t* data, uint32 #ifdef _DEBUG texture.texture->setName(reinterpret_cast(g_memory.Translate(pictureData->name + 2))); #endif + XXH64_hash_t hash = XXH3_64bits(data, dataSize); - DiffPatchTexture(texture, data, dataSize); + // The whale in Cool Edge has a 2D texture assigned as a cubemap which makes it not display in recomp. + // The hardware duplicates the first face to the remaining 6 faces, so to simulate that we'll recreate the asset. + bool forceCubeMap = (dataSize == 0xAB38) && (hash == 0x160E9E250FDE88A9); + if (forceCubeMap) + { + GuestTexture recreatedCubeMapTexture(ResourceType::Texture); + if (LoadTexture(recreatedCubeMapTexture, data, dataSize, {}, true)) + texture.recreatedCubeMapTexture = std::make_unique(std::move(recreatedCubeMapTexture)); + } + + DiffPatchTexture(texture, data, dataSize, hash); pictureData->texture = g_memory.MapVirtual(g_userHeap.AllocPhysical(std::move(texture))); pictureData->type = 0; diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index 9013c685..3a394be8 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -158,6 +158,7 @@ struct GuestTexture : GuestBaseTexture void* mappedMemory = nullptr; std::unique_ptr framebuffer; std::unique_ptr patchedTexture; + std::unique_ptr recreatedCubeMapTexture; struct GuestSurface* sourceSurface = nullptr; }; diff --git a/UnleashedRecomp/mod/mod_loader.cpp b/UnleashedRecomp/mod/mod_loader.cpp index dd33c56d..6fc3aec5 100644 --- a/UnleashedRecomp/mod/mod_loader.cpp +++ b/UnleashedRecomp/mod/mod_loader.cpp @@ -191,7 +191,10 @@ void ModLoader::Init() { std::string includeDirU8 = modIni.getString("Main", fmt::format("IncludeDir{}", j), ""); if (!includeDirU8.empty()) + { + std::replace(includeDirU8.begin(), includeDirU8.end(), '\\', '/'); mod.includeDirs.emplace_back(modDirectoryPath / std::u8string_view((const char8_t*)includeDirU8.c_str())); + } } if (!foundModSaveFilePath) diff --git a/UnleashedRecomp/patches/aspect_ratio_patches.cpp b/UnleashedRecomp/patches/aspect_ratio_patches.cpp index e42c5828..d1780b16 100644 --- a/UnleashedRecomp/patches/aspect_ratio_patches.cpp +++ b/UnleashedRecomp/patches/aspect_ratio_patches.cpp @@ -711,8 +711,6 @@ static const xxHashMap g_modifiers = // ui_shop { HashStr("ui_shop/footer/shop_footer"), { ALIGN_BOTTOM } }, - { HashStr("ui_shop/header/ring"), { ALIGN_TOP } }, - { HashStr("ui_shop/header/shop_nametag"), { ALIGN_TOP } }, // ui_start { HashStr("ui_start/Clear/position/bg/bg_1"), { STRETCH } }, diff --git a/tools/XenosRecomp b/tools/XenosRecomp index 855a5a8c..4897cf7e 160000 --- a/tools/XenosRecomp +++ b/tools/XenosRecomp @@ -1 +1 @@ -Subproject commit 855a5a8c51ea5f84baecbf4fc87c182795d482c9 +Subproject commit 4897cf7ef2070120310c28a1a672b427d745dad8