use super::{
BatchGroupInfo, Error, IndexedBatchError, J2kDecodeWarning, PreparedBatchGroup, Rect, Surface,
};
#[cfg(any(test, target_os = "macos"))]
use super::{BatchLayout, PixelFormat};
#[cfg(target_os = "macos")]
use super::{Buffer, ResidentMetalImage};
#[cfg(any(test, target_os = "macos"))]
pub(super) fn validate_group_contract(info: &BatchGroupInfo) -> Result<PixelFormat, Error> {
if !matches!(info.layout, BatchLayout::Nchw | BatchLayout::Nhwc) {
return Err(Error::UnsupportedMetalRequest {
reason: "J2K Metal batch received an unknown output layout",
});
}
info.native_pixel_format()
.ok_or(Error::UnsupportedMetalRequest {
reason: "J2K Metal batch metadata contains an unsupported color/sample combination",
})
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MetalBatchGroupCompletion {
pub(super) decoded_rects: Vec<Rect>,
pub(super) warnings: Vec<Vec<J2kDecodeWarning>>,
}
impl MetalBatchGroupCompletion {
#[cfg(target_os = "macos")]
pub(super) fn from_prepared(group: &PreparedBatchGroup) -> Result<Self, Error> {
let mut budget =
crate::batch_allocation::BatchMetadataBudget::new("J2K Metal group completion");
let mut decoded_rects =
budget.try_vec(group.images().len(), "J2K Metal completed rectangles")?;
let mut warnings =
budget.try_vec(group.images().len(), "J2K Metal completed warning owners")?;
for image in group.images() {
decoded_rects.push(image.plan().output_rect());
let mut image_warnings = Vec::new();
if image.used_lenient_metadata_recovery() {
image_warnings.push(J2kDecodeWarning::LenientMetadataRecovery);
}
warnings.push(image_warnings);
}
Ok(Self {
decoded_rects,
warnings,
})
}
#[must_use]
pub fn decoded_rects(&self) -> &[Rect] {
&self.decoded_rects
}
#[must_use]
pub fn warnings(&self) -> &[Vec<J2kDecodeWarning>] {
&self.warnings
}
#[must_use]
pub fn into_parts(self) -> (Vec<Rect>, Vec<Vec<J2kDecodeWarning>>) {
(self.decoded_rects, self.warnings)
}
}
#[cfg(target_os = "macos")]
#[derive(Clone)]
pub struct MetalResidentBatch {
pub(super) storage: ResidentMetalImage,
}
#[cfg(target_os = "macos")]
impl core::fmt::Debug for MetalResidentBatch {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MetalResidentBatch")
.field("device_registry_id", &self.device_registry_id())
.field("byte_offset", &self.byte_offset())
.field("byte_len", &self.byte_len())
.field("image_count", &self.image_count())
.field("image_stride_bytes", &self.image_stride_bytes())
.finish_non_exhaustive()
}
}
#[cfg(target_os = "macos")]
impl MetalResidentBatch {
#[must_use]
pub const fn byte_offset(&self) -> usize {
self.storage.layout().byte_offset()
}
#[must_use]
pub const fn byte_len(&self) -> usize {
self.storage.layout().byte_len()
}
#[must_use]
pub const fn image_count(&self) -> usize {
self.storage.layout().image_count()
}
#[must_use]
pub const fn image_stride_bytes(&self) -> usize {
self.storage.layout().image_stride_bytes()
}
#[must_use]
pub fn device_registry_id(&self) -> u64 {
self.storage.device_registry_id()
}
#[must_use]
pub unsafe fn metal_buffer(&self) -> &Buffer {
unsafe { self.storage.raw_buffer() }
}
}
pub struct MetalBatchGroup {
pub(super) info: BatchGroupInfo,
pub(super) source_indices: Vec<usize>,
pub(super) decoded_rects: Vec<Rect>,
pub(super) warnings: Vec<Vec<J2kDecodeWarning>>,
pub(super) surfaces: Vec<Surface>,
#[cfg(target_os = "macos")]
pub(super) resident_batch: MetalResidentBatch,
}
pub type MetalBatchGroupParts = (
BatchGroupInfo,
Vec<usize>,
Vec<Rect>,
Vec<Vec<J2kDecodeWarning>>,
Vec<Surface>,
);
#[derive(Debug, thiserror::Error)]
#[error("Metal batch group containing source indices {source_indices:?} failed: {source}")]
pub struct MetalBatchGroupError {
pub(super) source_indices: Vec<usize>,
#[source]
pub(super) source: Box<Error>,
}
impl MetalBatchGroupError {
#[allow(
clippy::disallowed_methods,
reason = "error construction preserves its infallible public signature while retaining affected source indices"
)]
pub(super) fn new(group: &PreparedBatchGroup, source: Error) -> Self {
Self {
source_indices: group.source_indices().to_vec(),
source: Box::new(source),
}
}
#[must_use]
pub fn source_indices(&self) -> &[usize] {
&self.source_indices
}
#[must_use]
pub fn source(&self) -> &Error {
&self.source
}
#[must_use]
pub fn into_parts(self) -> (Vec<usize>, Error) {
(self.source_indices, *self.source)
}
}
impl core::fmt::Debug for MetalBatchGroup {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_struct("MetalBatchGroup");
debug
.field("info", &self.info)
.field("source_indices", &self.source_indices)
.field("decoded_rects", &self.decoded_rects)
.field("warnings", &self.warnings)
.field("surface_count", &self.surfaces.len());
#[cfg(target_os = "macos")]
debug.field("resident_batch", &self.resident_batch);
debug.finish()
}
}
impl MetalBatchGroup {
pub fn info(&self) -> &BatchGroupInfo {
&self.info
}
pub fn source_indices(&self) -> &[usize] {
&self.source_indices
}
pub fn decoded_rects(&self) -> &[Rect] {
&self.decoded_rects
}
pub fn warnings(&self) -> &[Vec<J2kDecodeWarning>] {
&self.warnings
}
pub fn surfaces(&self) -> &[Surface] {
&self.surfaces
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn resident_batch(&self) -> Option<&MetalResidentBatch> {
Some(&self.resident_batch)
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn into_resident_batch(self) -> Option<MetalResidentBatch> {
Some(self.resident_batch)
}
pub fn into_parts(self) -> MetalBatchGroupParts {
(
self.info,
self.source_indices,
self.decoded_rects,
self.warnings,
self.surfaces,
)
}
}
#[derive(Debug)]
pub struct MetalBatchDecodeResult {
pub(super) groups: Vec<MetalBatchGroup>,
pub(super) errors: Vec<IndexedBatchError>,
pub(super) group_errors: Vec<MetalBatchGroupError>,
}
impl MetalBatchDecodeResult {
pub fn groups(&self) -> &[MetalBatchGroup] {
&self.groups
}
pub fn errors(&self) -> &[IndexedBatchError] {
&self.errors
}
#[must_use]
pub fn group_errors(&self) -> &[MetalBatchGroupError] {
&self.group_errors
}
#[must_use]
pub fn into_parts(
self,
) -> (
Vec<MetalBatchGroup>,
Vec<IndexedBatchError>,
Vec<MetalBatchGroupError>,
) {
(self.groups, self.errors, self.group_errors)
}
}