Skip to content

refactor(rocJPEG): route single-image decode through the batched kernels - #12976

Merged
AryanSalmanpour merged 3 commits into
developfrom
users/asalmanp/single_image_hip_kernels_cleanup
Oct 8, 2026
Merged

AryanSalmanpour merged 3 commits into
developfrom
users/asalmanp/single_image_hip_kernels_cleanup

Conversation

@AryanSalmanpour

@AryanSalmanpour AryanSalmanpour commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Motivation

Commit 90d4836 added a batched color-conversion kernel family, including two unified YUV->RGB kernels that cover NV12/YUV444/YUV440/YUYV/YUV400 via a per-image layout tag selected by hipBlockIdx_z. It deliberately left the single-image kernels in place, so the library carried two complete and independently maintained implementations of the same conversion math.

That duplication is a maintenance hazard: the BT.709 -> BT.601 change in c0b583c had to be applied in ten separate kernel bodies, and any future coefficient or layout fix would have to be too.

A single image is just a batch of one, so the batched path already subsumes the single-image path entirely.

Technical Details

Kernels (rocjpeg_hip_kernels.{h,cpp})

  • Removed all 14 single-image kernels and their host launchers: the five YUV->RGB variants and their planar counterparts, plus ColorConvertRGBAToRGB, ConvertInterleavedUVToPlanarUV, ExtractYFromPackedYUYV and ConvertPackedYUYVToPlanarYUV.
  • The batched kernels are now the only conversion kernels in the library. Comments that referred to the single-image bodies as the source of truth were restated accordingly.

Decoder (rocjpeg_decoder.{h,cpp})

  • Decode and DecodeSync now call FinalizeDecodeBatched(..., 1) directly.
  • Removed FinalizeDecode. It had become a pass-through whose null guard was unreachable from both call sites, and FunctionExitLog is a no-op macro, so no logging coverage is lost.
  • Removed the four per-image output producers that only FinalizeDecode used: ColorConvertToRGB, ColorConvertToRGBPlanar, GetPlanarYUVOutputFormat and GetYOutputFormat. The Accumulate* helpers are now the only implementation.

Behavior is unchanged. Error semantics are preserved: FinalizeDecodeBatched releases surfaces only on the success path, and both single-image callers still call SetSurfaceAsIdle on failure.

Version bumped to 1.12.0. No public API or ABI change; the soname major stays at 1.

Output is unchanged. Error semantics are preserved: FinalizeDecodeBatched
releases surfaces only on the success path, and both single-image callers
still call SetSurfaceAsIdle on failure.

Locking changes observably, however. Decode() now acquires finalize_mutex_
while still holding mutex_, so a concurrent DecodeBatchedSync can stall
Decode() before it starts work, extending the window in which Decode() holds
mutex_ and therefore blocks DecodeAsync submissions. Lock order is consistent
across all five entry points -- Decode and DecodeBatched take mutex_ then
finalize_mutex_, DecodeSync and DecodeBatchedSync release mutex_ before taking
finalize_mutex_, and nothing takes finalize_mutex_ then mutex_ -- so there is
no deadlock. The added serialization is arguably a correctness improvement:
previously Decode()'s kernel launches shared hip_stream_ with a concurrent
batched finalize with no serialization at all.

Issue Tracking

JIRA ID: AICV-314

Test Plan

Test plan
rocjpeg ctest

  • Single-image decode now pays one hipMemcpyAsync of the param struct plus a
    device-memory indirection in the kernel; the hipMalloc is amortized, since
    BatchedKernelParams grows its device buffer on demand and keeps it. Measured
    at 200 iterations on a 3840x2160 4:2:0 image: RGB 11.36 ms vs 12.05 ms base,
    native 11.42 ms vs 11.08 ms base -- lost in the noise against VCN decode
    time. Noted as understood and accepted rather than unexamined.

Test Result

rocjpeg ctest shoudl pass.

Submission Checklist

Motivation

Commit 90d4836 added a batched color-conversion kernel family, including
two unified YUV->RGB kernels that cover NV12/YUV444/YUV440/YUYV/YUV400 via a
per-image layout tag selected by hipBlockIdx_z. It deliberately left the
single-image kernels in place, so the library carried two complete and
independently maintained implementations of the same conversion math.

That duplication is a maintenance hazard: the BT.709 -> BT.601 change in
c0b583c had to be applied in ten separate kernel bodies, and any future
coefficient or layout fix would have to be too.

A single image is just a batch of one, so the batched path already subsumes
the single-image path entirely.

Technical details

Kernels (rocjpeg_hip_kernels.{h,cpp})
- Removed all 14 single-image kernels and their host launchers: the five
  YUV->RGB variants and their planar counterparts, plus ColorConvertRGBAToRGB,
  ConvertInterleavedUVToPlanarUV, ExtractYFromPackedYUYV and
  ConvertPackedYUYVToPlanarYUV.
- The batched kernels are now the only conversion kernels in the library.
  Comments that referred to the single-image bodies as the source of truth
  were restated accordingly.

Decoder (rocjpeg_decoder.{h,cpp})
- Decode and DecodeSync now call FinalizeDecodeBatched(..., 1) directly.
- Removed FinalizeDecode. It had become a pass-through whose null guard was
  unreachable from both call sites, and FunctionExitLog is a no-op macro, so
  no logging coverage is lost.
- Removed the four per-image output producers that only FinalizeDecode used:
  ColorConvertToRGB, ColorConvertToRGBPlanar, GetPlanarYUVOutputFormat and
  GetYOutputFormat. The Accumulate* helpers are now the only implementation.

Behavior is unchanged. Error semantics are preserved: FinalizeDecodeBatched
releases surfaces only on the success path, and both single-image callers
still call SetSurfaceAsIdle on failure.

Version bumped to 1.12.0. No public API or ABI change; the soname major
stays at 1.

Test plan

- Fresh configure and build, clean with no unused-function warnings.
- ctest: 26/26 passing.
- Bit-exactness against a build of develop: decoded 4:4:4, 4:4:0, 4:2:2,
  4:2:0 and grayscale JPEGs across all five output formats, with and without
  a crop rectangle, through both libraries. 50 outputs per side, byte
  identical. Repeated for the async path (DecodeSync), 10 outputs, also
  byte identical.
- ROI note: this test GPU reports 0 VCN JPEG cores, so can_roi_decode is
  false and the crop runs exercised the software ROI offset math. The
  hardware-ROI branch needs a >=8-core part and was not covered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 00:14
@therock-pr-bot

therock-pr-bot Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

✅ All Policy Checks Passed

Check Status Details
📝 PR Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ⚠️ Warning Error: Source/code files changed without an accompanying unit test.
Expected: add at least one test file named like test_<name>.py / test_<name>.cpp (or <name>_test.*).
Current: code file(s) changed: projects/rocjpeg/src/rocjpeg_decoder.cpp, projects/rocjpeg/src/rocjpeg_decoder.h, projects/rocjpeg/src/rocjpeg_hip_kernels.cpp, projects/rocjpeg/src/rocjpeg_hip_kernels.h, projects/rocjpeg/src/rocjpeg_vaapi_decoder.cpp (+1 more); no test file found
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

🎉 All policy checks passed!

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Single-image decoding can hang indefinitely when JPEG-core discovery leaves the batched group size at zero.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Routes single-image rocJPEG decoding through the batched conversion pipeline, removing duplicate kernels and helpers.

Changes:

  • Uses FinalizeDecodeBatched(..., 1) for single-image decoding.
  • Removes redundant single-image kernels and output helpers.
  • Bumps rocJPEG to 1.12.0 and updates the changelog.
File Description
projects/​rocjpeg/​CMakeLists.txt Bumps version to 1.12.0.
projects/​rocjpeg/​CHANGELOG.md Documents the unified decode path.
projects/​rocjpeg/​src/​rocjpeg_decoder.cpp Routes single-image finalization through batching and removes duplicate helpers.
projects/​rocjpeg/​src/​rocjpeg_decoder.h Removes obsolete declarations and updates documentation.
projects/​rocjpeg/​src/​rocjpeg_hip_kernels.cpp Removes single-image kernel implementations.
projects/​rocjpeg/​src/​rocjpeg_hip_kernels.h Removes single-image prototypes and documents batched kernels.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Motivation

Review feedback on the previous commit: routing the synchronous single-image
path through FinalizeDecodeBatched introduces a hang when JPEG-core discovery
fails.

Confirmed by fault injection -- forcing num_jpeg_cores to 0 makes a
single-image decode hang indefinitely, stalling right after decode submission.

GetNumJpegCores() returns void and both of its failure paths only log, leaving
the zero-initialized count in place while InitializeDecoder still reports
success. The work-grouping loops advance by `i += num_jpeg_cores`, so a zero
stride never terminates.

Technical details

Fixed at the single producer of the value rather than at the loops, because
there are three such loops -- in FinalizeDecodeBatched, DecodeBatched and
DecodeBatchedAsync. The latter two carried the same latent hang before the
single-image path was ever routed through them.

- Default the spec to a core count of 1, so the early-return path on
  amdgpu_device_initialize failure leaves a usable stride.
- Clamp a successful query that reports 0 to 1 as well; amdgpu_query_hw_ip_count
  can return success with a zero count.
- Document the ">= 1" invariant on the field itself so it is not reintroduced.

A GPU genuinely without JPEG cores cannot reach this code: CreateDecoderConfig
runs first and fails with ROCJPEG_STATUS_HW_JPEG_DECODER_NOT_SUPPORTED unless
VA-API advertises VAEntrypointVLD for VAProfileJPEGBaseline. So a zero here
means discovery disagreed with VA-API about hardware that demonstrably decodes
JPEG -- a bad reading, not a real capability. The previous behavior did not
preserve a meaningful zero either: it produced a 1-surface pool and then hung.

The capability flags still key off the raw queried value, so can_roi_decode and
can_convert_to_rgb remain false under the fallback. The library never claims
hardware ROI or hardware CSC it does not have; it degrades to one image at a
time with software ROI.

Discovery failure is deliberately not made fatal. VA-API submission still works
in that state, so failing rocJpegCreate would turn a degraded-but-working setup
into a dead one. The fallback is logged.

Test plan

- Fault-injected both failure modes (device-init failure, and a successful
  query returning zero cores) across jpegdecode, jpegdecodeasync and
  jpegdecodebatched: all six combinations exit 0, where the single-image case
  previously hung. Injection hooks were removed before committing.
- Output under the fallback is byte-identical to the normal path.
- ctest: 26/26 passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

GPU conversion equivalence and hardware-dependent failure behavior require final human review and rocJPEG test execution.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Comment thread projects/rocjpeg/CHANGELOG.md Resolved

@rrawther rrawther left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues found

[Minor] The changelog does not describe this PR, and retroactively marks 1.11.0 as released

CHANGELOG.md:6-16

At the base commit, ## (unreleased) rocJPEG 1.11.0 held two entries: the BT.601 change (from c0b583cf8f5) and "Added HIP color-conversion kernels in the batched decode path" (from 90d48366c71). This PR drops the (unreleased) marker from 1.11.0 — claiming a release that never happened — and moves the batched-kernels entry up into the new 1.12.0 section, re-attributing 90d48366c71's work to a version it wasn't in. Meanwhile this PR's own change (single-image decode now goes through the batched kernels; 14 kernels removed) gets no entry at all.

Since 1.11.0 never shipped, the straightforward fix is to leave 1.11.0 (unreleased) with both of its original entries, drop the version bump, and add a ### Changed line there for this refactor — or, if you want the bump, keep 1.11.0 intact and put this PR's actual change under 1.12.0.

[Nit] SetPoolSize changes on the discovery-failure path

src/rocjpeg_vaapi_decoder.cpp:547

SetPoolSize(5 * num_jpeg_cores + 1) previously evaluated to 1 when core discovery failed (count was 0); it now evaluates to 6. That's a strict improvement and almost certainly intended, but it is a second behavior change riding along with the zero-guard and isn't mentioned in the commit message.

Suggestions

Decode() now acquires finalize_mutex_ while holding mutex_

src/rocjpeg_decoder.cpp:133 + src/rocjpeg_decoder.cpp:246

I checked the lock order across all five entry points: Decode/DecodeBatched take mutex_ → finalize_mutex_; DecodeSync/DecodeBatchedSync release mutex_ before taking finalize_mutex_; nothing takes finalize_mutex_ then mutex_. No deadlock. The observable change is that a concurrent DecodeBatchedSync can now stall Decode() before it starts work, extending the window in which Decode() holds mutex_ and thus blocks DecodeAsync submissions. This is arguably a correctness improvement — previously Decode()'s kernel launches shared hip_stream_ with a concurrent batched finalize with no serialization — but it is a real scheduling change that the "Behavior is unchanged" claim glosses over. Worth one sentence in the commit message.

Per-decode param upload on the single-image hot path

src/rocjpeg_decoder.h:71-82

Single-image decode now pays one hipMemcpyAsync of the param struct plus a device-memory indirection in the kernel. The hipMalloc is amortized (capacity grows on demand and persists). I measured this and it's lost in the noise against VCN decode time (200 iterations, 3840x2160 4:2:0: RGB 11.36 ms PR vs 12.05 ms base; native 11.42 ms PR vs 11.08 ms base) — just noting it as understood-and-accepted rather than unexamined.

Claims I verified rather than took on faith

  • "the null guard was unreachable from both call sites" — correct. Decode() null-checks all three pointers at rocjpeg_decoder.cpp:134, and GetJpegStreamParameters() returns &jpeg_stream_parameters_ (rocjpeg_parser.h:231), which can never be null. DecodeSync() passes the address of a value member.
  • Error semantics preserved — FinalizeDecodeBatched releases surfaces only after LaunchBatchedParams and hipStreamSynchronize both succeed; both single-image callers still call SetSurfaceAsIdle on failure. With batch_size == 1 there is exactly one sub-batch, so no double-release.
  • Format coverage is complete — every case the removed ColorConvertToRGB/ColorConvertToRGBPlanar/GetPlanarYUVOutputFormat/GetYOutputFormat handled has an Accumulate* equivalent, including VA_FOURCC_RGBA (→ b_rgba_rgb_) and VA_FOURCC_RGBP (→ three CopyChannels). All six batched buffers are launched in LaunchBatchedParams.
  • YUV400 is bit-exact, not approximately equal — the removed kernel wrote R=G=B=Y directly; the unified kernel feeds neutral chroma 128 through the conversion. With the full-range BT.601 coefficients in rocjpeg_hip_kernels.h:49-54 and no studio-swing Y offset, fmaf(c, 0.0f, Y) == Y exactly. Equivalent.
  • Block dims match — removed YUV→RGB launchers used 16x4, batched uses 16x4; removed RGBA→RGB used 16x16, batched uses 16x16. Grid extent formulas identical.
  • No dangling references to any of the 14 removed symbols anywhere in the repo.

Test coverage gaps, for the record

I built bb18b69d6c8~1 in a separate worktree and ran an identical matrix against both builds — 5 output formats (native/yuv_planar/y/rgb/rgb_planar) x crop and no-crop x 4 decode APIs (single, batched, async, batched-async) — and compared all 120 output files: byte-identical, zero mismatches. Negative API + fuzz test also passes.

However, the repo's data/images/ only contains 4:2:0, 4:2:2 and 4:0:0 samples, so the YUV444 (444P) and YUV440 (422V) layout tags in the unified kernel were not exercised. The RGBA/RGBP surface paths also weren't — they require VCN hardware CSC (>=8 JPEG cores + ROCJPEG_ENABLE_VCN_HW_CSC=1), and my test machine is an RX 7900 XT. Both gaps are pre-existing in the ctest suite, not introduced here, but they do mean three of the six batched kernels now have no single-image regression coverage at all once the alternative implementation is gone.

@AryanSalmanpour

Copy link
Copy Markdown
Member Author

Issues found

[Minor] The changelog does not describe this PR, and retroactively marks 1.11.0 as released

CHANGELOG.md:6-16

At the base commit, ## (unreleased) rocJPEG 1.11.0 held two entries: the BT.601 change (from c0b583cf8f5) and "Added HIP color-conversion kernels in the batched decode path" (from 90d48366c71). This PR drops the (unreleased) marker from 1.11.0 — claiming a release that never happened — and moves the batched-kernels entry up into the new 1.12.0 section, re-attributing 90d48366c71's work to a version it wasn't in. Meanwhile this PR's own change (single-image decode now goes through the batched kernels; 14 kernels removed) gets no entry at all.

Since 1.11.0 never shipped, the straightforward fix is to leave 1.11.0 (unreleased) with both of its original entries, drop the version bump, and add a ### Changed line there for this refactor — or, if you want the bump, keep 1.11.0 intact and put this PR's actual change under 1.12.0.

[Nit] SetPoolSize changes on the discovery-failure path

src/rocjpeg_vaapi_decoder.cpp:547

SetPoolSize(5 * num_jpeg_cores + 1) previously evaluated to 1 when core discovery failed (count was 0); it now evaluates to 6. That's a strict improvement and almost certainly intended, but it is a second behavior change riding along with the zero-guard and isn't mentioned in the commit message.

Suggestions

Decode() now acquires finalize_mutex_ while holding mutex_

src/rocjpeg_decoder.cpp:133 + src/rocjpeg_decoder.cpp:246

I checked the lock order across all five entry points: Decode/DecodeBatched take mutex_ → finalize_mutex_; DecodeSync/DecodeBatchedSync release mutex_ before taking finalize_mutex_; nothing takes finalize_mutex_ then mutex_. No deadlock. The observable change is that a concurrent DecodeBatchedSync can now stall Decode() before it starts work, extending the window in which Decode() holds mutex_ and thus blocks DecodeAsync submissions. This is arguably a correctness improvement — previously Decode()'s kernel launches shared hip_stream_ with a concurrent batched finalize with no serialization — but it is a real scheduling change that the "Behavior is unchanged" claim glosses over. Worth one sentence in the commit message.

Per-decode param upload on the single-image hot path

src/rocjpeg_decoder.h:71-82

Single-image decode now pays one hipMemcpyAsync of the param struct plus a device-memory indirection in the kernel. The hipMalloc is amortized (capacity grows on demand and persists). I measured this and it's lost in the noise against VCN decode time (200 iterations, 3840x2160 4:2:0: RGB 11.36 ms PR vs 12.05 ms base; native 11.42 ms PR vs 11.08 ms base) — just noting it as understood-and-accepted rather than unexamined.

Claims I verified rather than took on faith

  • "the null guard was unreachable from both call sites" — correct. Decode() null-checks all three pointers at rocjpeg_decoder.cpp:134, and GetJpegStreamParameters() returns &jpeg_stream_parameters_ (rocjpeg_parser.h:231), which can never be null. DecodeSync() passes the address of a value member.
  • Error semantics preserved — FinalizeDecodeBatched releases surfaces only after LaunchBatchedParams and hipStreamSynchronize both succeed; both single-image callers still call SetSurfaceAsIdle on failure. With batch_size == 1 there is exactly one sub-batch, so no double-release.
  • Format coverage is complete — every case the removed ColorConvertToRGB/ColorConvertToRGBPlanar/GetPlanarYUVOutputFormat/GetYOutputFormat handled has an Accumulate* equivalent, including VA_FOURCC_RGBA (→ b_rgba_rgb_) and VA_FOURCC_RGBP (→ three CopyChannels). All six batched buffers are launched in LaunchBatchedParams.
  • YUV400 is bit-exact, not approximately equal — the removed kernel wrote R=G=B=Y directly; the unified kernel feeds neutral chroma 128 through the conversion. With the full-range BT.601 coefficients in rocjpeg_hip_kernels.h:49-54 and no studio-swing Y offset, fmaf(c, 0.0f, Y) == Y exactly. Equivalent.
  • Block dims match — removed YUV→RGB launchers used 16x4, batched uses 16x4; removed RGBA→RGB used 16x16, batched uses 16x16. Grid extent formulas identical.
  • No dangling references to any of the 14 removed symbols anywhere in the repo.

Test coverage gaps, for the record

I built bb18b69d6c8~1 in a separate worktree and ran an identical matrix against both builds — 5 output formats (native/yuv_planar/y/rgb/rgb_planar) x crop and no-crop x 4 decode APIs (single, batched, async, batched-async) — and compared all 120 output files: byte-identical, zero mismatches. Negative API + fuzz test also passes.

However, the repo's data/images/ only contains 4:2:0, 4:2:2 and 4:0:0 samples, so the YUV444 (444P) and YUV440 (422V) layout tags in the unified kernel were not exercised. The RGBA/RGBP surface paths also weren't — they require VCN hardware CSC (>=8 JPEG cores + ROCJPEG_ENABLE_VCN_HW_CSC=1), and my test machine is an RX 7900 XT. Both gaps are pre-existing in the ctest suite, not introduced here, but they do mean three of the six batched kernels now have no single-image regression coverage at all once the alternative implementation is gone.

@rrawther Thanks for the review. None of the comments above require any code changes; they only request updates to the commit message. Regarding the changelog, it was intentionally left blank because the changes in this PR are transparent to the user and do not introduce any user-visible behavior changes.

I have also updated the commit message based on the suggestions provided. As for the lack of YUV444/YUV440 test images, this is a known pre-existing issue and is not specific to this PR. Support for those test images will be added in upcoming PRs.

@AryanSalmanpour
AryanSalmanpour merged commit 4e9e32b into develop Oct 8, 2026
85 of 88 checks passed
@AryanSalmanpour
AryanSalmanpour deleted the users/asalmanp/single_image_hip_kernels_cleanup branch October 8, 2026 23:03
@systems-assistant

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

anujshuk-amd pushed a commit that referenced this pull request Oct 12, 2026
…els (#12976)

## Motivation

Commit 90d4836 added a batched color-conversion kernel family,
including two unified YUV->RGB kernels that cover
NV12/YUV444/YUV440/YUYV/YUV400 via a per-image layout tag selected by
hipBlockIdx_z. It deliberately left the single-image kernels in place,
so the library carried two complete and independently maintained
implementations of the same conversion math.

That duplication is a maintenance hazard: the BT.709 -> BT.601 change in
c0b583c had to be applied in ten separate kernel bodies, and any
future coefficient or layout fix would have to be too.

A single image is just a batch of one, so the batched path already
subsumes the single-image path entirely.


## Technical Details

Kernels (rocjpeg_hip_kernels.{h,cpp})
- Removed all 14 single-image kernels and their host launchers: the five
YUV->RGB variants and their planar counterparts, plus
ColorConvertRGBAToRGB, ConvertInterleavedUVToPlanarUV,
ExtractYFromPackedYUYV and ConvertPackedYUYVToPlanarYUV.
- The batched kernels are now the only conversion kernels in the
library. Comments that referred to the single-image bodies as the source
of truth were restated accordingly.

Decoder (rocjpeg_decoder.{h,cpp})
- Decode and DecodeSync now call FinalizeDecodeBatched(..., 1) directly.
- Removed FinalizeDecode. It had become a pass-through whose null guard
was unreachable from both call sites, and FunctionExitLog is a no-op
macro, so no logging coverage is lost.
- Removed the four per-image output producers that only FinalizeDecode
used: ColorConvertToRGB, ColorConvertToRGBPlanar,
GetPlanarYUVOutputFormat and GetYOutputFormat. The Accumulate* helpers
are now the only implementation.

Behavior is unchanged. Error semantics are preserved:
FinalizeDecodeBatched releases surfaces only on the success path, and
both single-image callers still call SetSurfaceAsIdle on failure.

Version bumped to 1.12.0. No public API or ABI change; the soname major
stays at 1.

Output is unchanged. Error semantics are preserved:
FinalizeDecodeBatched
releases surfaces only on the success path, and both single-image
callers
still call SetSurfaceAsIdle on failure.

Locking changes observably, however. Decode() now acquires
finalize_mutex_
while still holding mutex_, so a concurrent DecodeBatchedSync can stall
Decode() before it starts work, extending the window in which Decode()
holds
mutex_ and therefore blocks DecodeAsync submissions. Lock order is
consistent
across all five entry points -- Decode and DecodeBatched take mutex_
then
finalize_mutex_, DecodeSync and DecodeBatchedSync release mutex_ before
taking
finalize_mutex_, and nothing takes finalize_mutex_ then mutex_ -- so
there is
no deadlock. The added serialization is arguably a correctness
improvement:
previously Decode()'s kernel launches shared hip_stream_ with a
concurrent
batched finalize with no serialization at all.

## Issue Tracking

JIRA ID: AICV-314

## Test Plan

Test plan
rocjpeg ctest

- Single-image decode now pays one hipMemcpyAsync of the param struct
plus a
device-memory indirection in the kernel; the hipMalloc is amortized,
since
BatchedKernelParams grows its device buffer on demand and keeps it.
Measured
at 200 iterations on a 3840x2160 4:2:0 image: RGB 11.36 ms vs 12.05 ms
base,
native 11.42 ms vs 11.08 ms base -- lost in the noise against VCN decode
  time. Noted as understood and accepted rather than unexamined.
  
## Test Result

rocjpeg ctest shoudl pass.


## Submission Checklist

- [ ] Look over the contributing guidelines at
https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants