From 359f1d9beb3ee086b4a970dbce7f4a97462f7d9c Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:10:02 +0200 Subject: [PATCH 1/3] h264: Improve H264DECGetImageSize and fix a regression --- src/Cafe/OS/libs/h264_avc/H264Dec.cpp | 119 +++++++++++++++++- src/Cafe/OS/libs/h264_avc/parser/H264Parser.h | 5 +- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp index d03ca553..ec88af7c 100644 --- a/src/Cafe/OS/libs/h264_avc/H264Dec.cpp +++ b/src/Cafe/OS/libs/h264_avc/H264Dec.cpp @@ -189,9 +189,114 @@ namespace H264 return H264DEC_STATUS::BAD_STREAM; } + struct H264Dec_SeqParameterSet + { + // for now this contains only the fields we care about + uint32 pic_width_in_luma_samples{}; + uint32 pic_height_in_map_units_x16{}; + uint8 frame_mbs_only_flag{}; + }; + + void H264Dec_parseScalingList(RBSPInputBitstream& stream, sint32 size) + { + sint32 lastScale = 8; + sint32 nextScale = 8; + for (sint32 i = 0; i < size; i++) + { + if (nextScale != 0) + { + sint32 deltaScale = stream.readSV_E(); + nextScale = (lastScale + deltaScale + 256) % 256; + } + if (nextScale != 0) + lastScale = nextScale; + } + } + + bool H264Dec_parseSequencePS(RBSPInputBitstream& stream, H264Dec_SeqParameterSet& ps) + { + ps = {}; + uint32 profileIdc = stream.readBits<8>(); + if (profileIdc != 66 && profileIdc != 77 && profileIdc != 100) + return false; + stream.readBits<8>(); // constraint_set flags + stream.readBits<8>(); // level_idc + uint32 seqParameterSetId = stream.readUV_E(); + if (seqParameterSetId >= 32) + return false; + if (profileIdc == 100) + { + uint32 chromaFormatIdc = stream.readUV_E(); + if (chromaFormatIdc > 1) + return false; + stream.readUV_E(); // bit_depth_luma_minus8 + stream.readUV_E(); // bit_depth_chroma_minus8 + stream.readBit(); // qpprime_y_zero_transform_bypass_flag + if (stream.readBit()) // seq_scaling_matrix_present_flag + { + for (sint32 i = 0; i < 8; i++) + { + if (stream.readBit()) + H264Dec_parseScalingList(stream, i < 6 ? 16 : 64); + } + } + } + uint32 log2MaxFrameNumMinus4 = stream.readUV_E(); + if (log2MaxFrameNumMinus4 > 12) + return false; + uint32 picOrderCntType = stream.readUV_E(); + if (picOrderCntType > 2) + return false; + if (picOrderCntType == 0) + { + uint32 log2MaxPicOrderCntLsbMinus4 = stream.readUV_E(); + if (log2MaxPicOrderCntLsbMinus4 > 12) + return false; + } + else if (picOrderCntType == 1) + { + stream.readBit(); // delta_pic_order_always_zero_flag + stream.readSV_E(); // offset_for_non_ref_pic + stream.readSV_E(); // offset_for_top_to_bottom_field + uint32 numRefFramesInPicOrderCntCycle = stream.readUV_E(); + if (numRefFramesInPicOrderCntCycle > 0xFF) + return false; + for (uint32 i = 0; i < numRefFramesInPicOrderCntCycle; i++) + stream.readSV_E(); + } + uint32 maxNumRefFrames = stream.readUV_E(); + if (maxNumRefFrames > 16) + return false; + stream.readBit(); // gaps_in_frame_num_value_allowed_flag + uint32 picWidthInMbsMinus1 = stream.readUV_E(); + uint32 width = 16 * (picWidthInMbsMinus1 + 1); + if ((width & 0xFFF0) < 0x10) + return false; + uint32 picHeightInMapUnitsMinus1 = stream.readUV_E(); + uint32 heightInMapUnitsX16 = 16 * (picHeightInMapUnitsMinus1 + 1); + if ((heightInMapUnitsX16 & 0xFFF0) < 0x10) + return false; + ps.pic_width_in_luma_samples = width; + ps.pic_height_in_map_units_x16 = heightInMapUnitsX16; + ps.frame_mbs_only_flag = stream.readBit(); + if (!ps.frame_mbs_only_flag) + stream.readBit(); // mb_adaptive_frame_field_flag + stream.readBit(); // direct_8x8_inference_flag + if (stream.readBit()) // frame_cropping_flag + { + stream.readUV_E(); // frame_crop_left_offset + stream.readUV_E(); // frame_crop_right_offset + stream.readUV_E(); // frame_crop_top_offset + stream.readUV_E(); // frame_crop_bottom_offset + } + stream.readBit(); // vui_parameters_present_flag + // vui parsing unnecessary as we currently dont need any of the fields + return true; + } + H264DEC_STATUS H264DECGetImageSize(uint8* stream, sint32 streamSize, sint32 offset, uint32be* outputWidth, uint32be* outputHeight) { - if (!stream || streamSize < 4 || offset < 0 || !outputWidth || !outputHeight || offset < streamSize) + if (!stream || streamSize < 4 || offset < 0 || !outputWidth || !outputHeight || offset >= streamSize) return H264DEC_STATUS::INVALID_PARAM; if ( (offset+4) >= streamSize ) return H264DEC_STATUS::INVALID_PARAM; @@ -217,15 +322,19 @@ namespace H264 cur++; continue; } - h264State_seq_parameter_set_t psp; - bool r = h264Parser_ParseSPS(cur+2, end-cur-2, psp); + uint8* spsStart = cur + 2; + uint32 spsLength = (uint32)(end - spsStart); + H264Dec_SeqParameterSet psp; + RBSPInputBitstream rbspStream(spsStart, spsLength, false); + bool r = H264Dec_parseSequencePS(rbspStream, psp); if(!r) { + cemuLog_log(LogType::Force, "H264DECGetImageSize: Invalid SPS data"); cemu_assert_suspicious(); // should not happen return H264DEC_STATUS::BAD_STREAM; } - *outputWidth = (psp.pic_width_in_mbs_minus1 + 1) * 16; - *outputHeight = (psp.pic_height_in_map_units_minus1 + 1) * 16; + *outputWidth = psp.pic_width_in_luma_samples; + *outputHeight = psp.pic_height_in_map_units_x16; if (!psp.frame_mbs_only_flag) *outputHeight *= 2; if (!*outputHeight || !*outputWidth) diff --git a/src/Cafe/OS/libs/h264_avc/parser/H264Parser.h b/src/Cafe/OS/libs/h264_avc/parser/H264Parser.h index 6f2b3cf6..2d5aaf1a 100644 --- a/src/Cafe/OS/libs/h264_avc/parser/H264Parser.h +++ b/src/Cafe/OS/libs/h264_avc/parser/H264Parser.h @@ -7,7 +7,7 @@ class RBSPInputBitstream public: RBSPInputBitstream() {}; - RBSPInputBitstream(uint8* stream, uint32 length) + RBSPInputBitstream(uint8* stream, uint32 length, bool removeEmulationByte = true) : m_removeEmulationByte(removeEmulationByte) { this->streamPtr = stream; this->streamLength = length; @@ -169,7 +169,7 @@ private: void _nextRBSPByte() { readIndex += sizeof(uint8); - if (readIndex >= 2 && streamPtr[readIndex - 2] == 0x00 && streamPtr[readIndex - 1] == 0x00 && streamPtr[readIndex] == 0x03) + if (readIndex >= 2 && streamPtr[readIndex - 2] == 0x00 && streamPtr[readIndex - 1] == 0x00 && streamPtr[readIndex] == 0x03 && m_removeEmulationByte) { readIndex += 1; currentRBSPByte = streamPtr[readIndex]; @@ -188,6 +188,7 @@ private: uint8 currentRBSPByte{}; sint32 bitIndex{}; bool errorFlag{}; + bool m_removeEmulationByte{}; }; class NALInputBitstream From 0e7e9ee632a177699402cf4e1ab8cf5aa7ad9f2d Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Wed, 1 Jul 2026 01:19:16 +0900 Subject: [PATCH 2/3] CI: homebrew - Untap aws and azure to silence warnings (#1972) --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c1afde6c..3aaf9a93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -230,6 +230,9 @@ jobs: - name: "Install system dependencies" run: | + # Removing azure/bicep and aws to silence warnings in Github Actions + brew uninstall bicep + brew untap aws/tap azure/bicep brew update brew install nasm automake libtool From c2e807f8a3ff90eaa4b6867e038c277226466c4e Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 30 Jun 2026 17:20:19 +0100 Subject: [PATCH 3/3] Metal: specify blit options when copying to textures (#1912) --- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 878bcb9e..4cfdde33 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -745,6 +745,31 @@ void MetalRenderer::texture_clearSlice(LatteTexture* hostTexture, sint32 sliceIn } } +MTL::BlitOption GetBlitOptionForTexture(const LatteTextureMtl* texture) { + switch (texture->GetTexture()->pixelFormat()) { + case MTL::PixelFormatDepth16Unorm: + case MTL::PixelFormatDepth32Float: + return MTL::BlitOptionDepthFromDepthStencil; + + case MTL::PixelFormatStencil8: + return MTL::BlitOptionStencilFromDepthStencil; + + case MTL::PixelFormatDepth24Unorm_Stencil8: + case MTL::PixelFormatDepth32Float_Stencil8: + // Can't copy both in one call — caller must specify. + // Default to depth + return MTL::BlitOptionDepthFromDepthStencil; + + default: + return MTL::BlitOptionNone; + } +} + +bool IsDepthStencilFormat(MTL::PixelFormat format) { + return format == MTL::PixelFormatDepth24Unorm_Stencil8 || + format == MTL::PixelFormatDepth32Float_Stencil8; +} + // TODO: do a cpu copy on Apple Silicon? void MetalRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 compressedImageSize) { @@ -774,9 +799,14 @@ void MetalRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, s memcpy(allocation.memPtr, pixelData, compressedImageSize); bufferAllocator.FlushReservation(allocation); - // TODO: specify blit options when copying to a depth stencil texture? // Copy the data from the temporary buffer to the texture - blitCommandEncoder->copyFromBuffer(allocation.mtlBuffer, allocation.bufferOffset, bytesPerRow, 0, MTL::Size(width, height, 1), textureMtl->GetTexture(), sliceIndex, mipIndex, MTL::Origin(0, 0, offsetZ)); + if (IsDepthStencilFormat(textureMtl->GetTexture()->pixelFormat())) { + // Metal doesn't allow copying depth and stencil data at the same time, so we need to do two copies for combined depth/stencil formats + blitCommandEncoder->copyFromBuffer(allocation.mtlBuffer, allocation.bufferOffset, bytesPerRow, 0, MTL::Size(width, height, 1), textureMtl->GetTexture(), sliceIndex, mipIndex, MTL::Origin(0, 0, offsetZ), MTL::BlitOptionDepthFromDepthStencil); + blitCommandEncoder->copyFromBuffer(allocation.mtlBuffer, allocation.bufferOffset, bytesPerRow, 0, MTL::Size(width, height, 1), textureMtl->GetTexture(), sliceIndex, mipIndex, MTL::Origin(0, 0, offsetZ), MTL::BlitOptionStencilFromDepthStencil); + } else { + blitCommandEncoder->copyFromBuffer(allocation.mtlBuffer, allocation.bufferOffset, bytesPerRow, 0, MTL::Size(width, height, 1), textureMtl->GetTexture(), sliceIndex, mipIndex, MTL::Origin(0, 0, offsetZ), GetBlitOptionForTexture(textureMtl)); + } //} }