diff --git a/build.zig b/build.zig index 54763f0..3a057a8 100644 --- a/build.zig +++ b/build.zig @@ -607,6 +607,7 @@ fn addPhiDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, cc: []const u const sources = [_][]const u8{ "src/phi/mic/main.c", "src/phi/mic/Blitter.c", + "src/phi/mic/BlitFormats.c", "src/phi/mic/Buffer.c", "src/phi/mic/CommandBuffer.c", "src/phi/mic/Daemon.c", @@ -648,7 +649,7 @@ fn addPhiDaemon(b: *std.Build, optimize: std.builtin.OptimizeMode, cc: []const u cmd.addFileArg(avx_object); } - cmd.addArgs(&.{ "-lscif", "-o" }); + cmd.addArgs(&.{ "-lscif", "-lm", "-o" }); return cmd.addOutputFileArg("phi_device.mic"); } diff --git a/src/phi/PhiQueue.zig b/src/phi/PhiQueue.zig index 83703fd..cdf2a63 100644 --- a/src/phi/PhiQueue.zig +++ b/src/phi/PhiQueue.zig @@ -18,6 +18,15 @@ pub const Interface = base.Queue; const ring_capacity: usize = @intCast(proto.PHI_QUEUE_RING_CAPACITY); const ring_capacity_u64: u64 = @intCast(ring_capacity); const shutdown_sequence = std.math.maxInt(u64); +const shutdown_timeout_ns = 5 * std.time.ns_per_s; +const shutdown_poll_ns = 10 * std.time.ns_per_ms; + +const CompletionShutdown = enum { + acknowledged, + stopped_without_acknowledgement, + timed_out, + wait_failed, +}; const PreparedSubmit = struct { wait_semaphores: std.ArrayList(*base.BinarySemaphore), @@ -63,6 +72,7 @@ pending: [ring_capacity]?PendingCompletion, error_state: ?VkError, shutting_down: bool, +completion_stopped: bool, remote_stopped: bool, pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, family_index: u32, flags: vk.DeviceQueueCreateFlags) VkError!*Interface { @@ -129,6 +139,7 @@ pub fn create(allocator: std.mem.Allocator, device: *base.Device, index: u32, fa .pending = [_]?PendingCompletion{null} ** ring_capacity, .error_state = null, .shutting_down = false, + .completion_stopped = false, .remote_stopped = false, }; @@ -141,27 +152,63 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void const io = interface.owner.io(); const device_allocator = interface.owner.device_allocator.allocator(); + var graceful_shutdown = true; waitIdle(interface) catch |err| { + graceful_shutdown = false; std.log.scoped(.PhiQueue).warn("Queue did not become idle during destruction: {s}", .{@errorName(err)}); }; - - var graceful_shutdown = true; + var mutex_locked = true; self.mutex.lock(io) catch { + mutex_locked = false; graceful_shutdown = false; }; - if (graceful_shutdown) { + var shutdown_next_sequence: u64 = 0; + var shutdown_completed_sequence: u64 = 0; + if (mutex_locked) { self.shutting_down = true; + shutdown_next_sequence = self.next_remote_sequence; + shutdown_completed_sequence = self.completed_sequence; self.condition.broadcast(io); self.mutex.unlock(io); + } + if (graceful_shutdown) { + std.log.scoped(.PhiQueue).info( + "Sending shutdown doorbell (next remote sequence {d}, completed {d})", + .{ shutdown_next_sequence, shutdown_completed_sequence }, + ); self.transport.sendQueueDoorbell(shutdown_sequence) catch |err| { graceful_shutdown = false; std.log.scoped(.PhiQueue).warn("Failed to send queue shutdown doorbell: {s}", .{@errorName(err)}); - self.transport.close(); }; - } else { - // Wake the blocking completion receiver before releasing queue storage - self.transport.close(); + + if (graceful_shutdown) { + switch (self.waitForCompletionShutdown(io)) { + .acknowledged => {}, + .stopped_without_acknowledgement => { + graceful_shutdown = false; + std.log.scoped(.PhiQueue).warn("Remote queue stopped without acknowledging shutdown", .{}); + }, + .timed_out => { + graceful_shutdown = false; + const progress = self.completionProgress(io); + std.log.scoped(.PhiQueue).warn( + "Timed out waiting for remote queue shutdown (next remote sequence {d}, completed {d}); closing SCIF endpoint", + .{ progress.next, progress.completed }, + ); + }, + .wait_failed => { + graceful_shutdown = false; + std.log.scoped(.PhiQueue).warn("Failed while waiting for remote queue shutdown; closing SCIF endpoint", .{}); + }, + } + } + } + + if (!graceful_shutdown) { + // Wake the blocking completion receiver before releasing queue storage. + // Keep libscif loaded until the receiver has returned from scif_recv. + self.transport.interrupt(); } self.completion_group.await(io) catch |err| { @@ -521,9 +568,16 @@ fn publish(self: *Self, prepared: *PreparedSubmit, fence: ?*base.Fence) VkError! } fn completionRunner(self: *Self) void { + defer self.markCompletionStopped(); + while (true) { - const completion = self.transport.receiveQueueCompletion() catch { - if (!self.isShuttingDown()) self.markLost(VkError.DeviceLost); + const completion = self.transport.receiveQueueCompletion() catch |err| { + if (!self.isShuttingDown()) { + std.log.scoped(.PhiQueue).err("Queue completion receive failed: {s}", .{@errorName(err)}); + self.markLost(VkError.DeviceLost); + } else { + std.log.scoped(.PhiQueue).warn("Queue completion receiver stopped without shutdown acknowledgement: {s}", .{@errorName(err)}); + } return; }; @@ -531,6 +585,7 @@ fn completionRunner(self: *Self) void { const io = self.interface.owner.io(); self.mutex.lock(io) catch return; self.remote_stopped = completion.status == proto.PHI_STATUS_OK; + std.log.scoped(.PhiQueue).info("Received remote queue shutdown acknowledgement (status {d})", .{completion.status}); self.condition.broadcast(io); self.mutex.unlock(io); return; @@ -574,7 +629,14 @@ fn completeOne(self: *Self, completion: proto.PhiQueueCompletion) void { if (pending.command_backing) |backing| allocator.free(backing); if (completion.status != proto.PHI_STATUS_OK or cleanup_failed) { - self.markLost(VkError.DeviceLost); + std.log.scoped(.PhiQueue).err( + "Queue completion {d} failed with remote status {d} (cleanup failed: {})", + .{ completion.sequence, completion.status, cleanup_failed }, + ); + // A remote command error is confined to this submission. The protocol + // stream and ring remain synchronized, so poisoning every later CTS + // submission would only hide the command that actually failed. + if (cleanup_failed) self.markLost(VkError.DeviceLost); failPending(&pending); } else if (self.hasError()) { failPending(&pending); @@ -649,6 +711,43 @@ fn hasError(self: *Self) bool { return self.error_state != null; } +fn completionProgress(self: *Self, io: std.Io) struct { next: u64, completed: u64 } { + self.mutex.lock(io) catch return .{ .next = 0, .completed = 0 }; + defer self.mutex.unlock(io); + return .{ .next = self.next_remote_sequence, .completed = self.completed_sequence }; +} + +fn waitForCompletionShutdown(self: *Self, io: std.Io) CompletionShutdown { + const deadline = std.Io.Clock.Timestamp.fromNow(io, .{ + .raw = .fromNanoseconds(shutdown_timeout_ns), + .clock = .awake, + }); + + while (true) { + self.mutex.lock(io) catch return .wait_failed; + const stopped = self.completion_stopped; + const acknowledged = self.remote_stopped; + self.mutex.unlock(io); + + if (stopped) return if (acknowledged) .acknowledged else .stopped_without_acknowledgement; + + const remaining = deadline.durationFromNow(io); + if (remaining.raw.nanoseconds <= 0) return .timed_out; + (std.Io.Clock.Duration{ + .raw = .fromNanoseconds(@min(remaining.raw.nanoseconds, shutdown_poll_ns)), + .clock = .awake, + }).sleep(io) catch return .wait_failed; + } +} + +fn markCompletionStopped(self: *Self) void { + const io = self.interface.owner.io(); + self.mutex.lock(io) catch return; + self.completion_stopped = true; + self.condition.broadcast(io); + self.mutex.unlock(io); +} + fn isShuttingDown(self: *Self) bool { const io = self.interface.owner.io(); self.mutex.lock(io) catch return true; diff --git a/src/phi/PhiTransport.zig b/src/phi/PhiTransport.zig index a550359..98958ac 100644 --- a/src/phi/PhiTransport.zig +++ b/src/phi/PhiTransport.zig @@ -12,6 +12,8 @@ const Self = @This(); epd: Endpoint, sequence: u64 = 1, mutex: std.Io.Mutex = .init, +endpoint_mutex: base.SpinMutex = .{}, +library_loaded: bool = true, instance: *base.Instance, node_id: u16, @@ -68,13 +70,25 @@ pub fn deinit(self: *Self) void { std.log.scoped(.PhiTransport).info("Closed connection", .{}); } -/// Close a transport without issuing an RPC shutdown. Queue transports switch -/// to a raw full-duplex doorbell protocol after setup and must use this path -pub fn close(self: *Self) void { - if (self.epd < 0) return; - - closeEndpoint(self.epd); +/// Close the endpoint so a thread blocked in SCIF receive wakes up. The SCIF +/// library stays loaded until `close`, because that thread may still be +/// returning through a dynamically loaded function. +pub fn interrupt(self: *Self) void { + self.endpoint_mutex.lock(); + const endpoint = self.epd; self.epd = -1; + self.endpoint_mutex.unlock(); + + if (endpoint >= 0) closeEndpoint(endpoint); +} + +/// Close a transport without issuing an RPC shutdown. Queue transports switch +/// to a raw full-duplex doorbell protocol after setup and must use this path. +pub fn close(self: *Self) void { + self.interrupt(); + if (!self.library_loaded) return; + + self.library_loaded = false; scif.unload(); } @@ -137,9 +151,10 @@ pub fn statusToErr(status: c_int) VkError { } fn writeAll(self: *Self, bytes: []const u8) VkError!void { + const endpoint = self.getEndpoint() orelse return VkError.DeviceLost; var offset: usize = 0; while (offset < bytes.len) { - const written = scif.send(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.send_block); + const written = scif.send(endpoint, bytes[offset..].ptr, bytes.len - offset, scif.send_block); if (written <= 0) { return VkError.DeviceLost; } @@ -148,9 +163,10 @@ fn writeAll(self: *Self, bytes: []const u8) VkError!void { } fn readAll(self: *Self, bytes: []u8) VkError!void { + const endpoint = self.getEndpoint() orelse return VkError.DeviceLost; var offset: usize = 0; while (offset < bytes.len) { - const read = scif.recv(self.epd, bytes[offset..].ptr, bytes.len - offset, scif.recv_block); + const read = scif.recv(endpoint, bytes[offset..].ptr, bytes.len - offset, scif.recv_block); if (read <= 0) { return VkError.DeviceLost; } @@ -158,6 +174,12 @@ fn readAll(self: *Self, bytes: []u8) VkError!void { } } +fn getEndpoint(self: *Self) ?Endpoint { + self.endpoint_mutex.lock(); + defer self.endpoint_mutex.unlock(); + return if (self.epd >= 0) self.epd else null; +} + fn closeEndpoint(endpoint: Endpoint) void { _ = scif.close(endpoint); } @@ -181,8 +203,9 @@ fn handshake(self: *Self) VkError!void { } pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 { + const endpoint = self.getEndpoint() orelse return VkError.DeviceLost; const offset = scif.register( - self.epd, + endpoint, memory.ptr, memory.len, 0, @@ -196,7 +219,8 @@ pub fn registerHostMemory(self: *Self, memory: []u8) VkError!u64 { } pub fn unregisterHostMemory(self: *Self, offset: u64, size: usize) VkError!void { - if (scif.unregister(self.epd, @intCast(offset), size) != 0) { + const endpoint = self.getEndpoint() orelse return VkError.DeviceLost; + if (scif.unregister(endpoint, @intCast(offset), size) != 0) { return VkError.Unknown; } } diff --git a/src/phi/mic/BlitFormats.c b/src/phi/mic/BlitFormats.c new file mode 100644 index 0000000..92b486b --- /dev/null +++ b/src/phi/mic/BlitFormats.c @@ -0,0 +1,1813 @@ +#include + +#include +#include +#include + +static uint16_t LoadU16(const uint8_t* map) +{ + uint16_t value; + memcpy(&value, map, sizeof(value)); + return value; +} + +static uint32_t LoadU32(const uint8_t* map) +{ + uint32_t value; + memcpy(&value, map, sizeof(value)); + return value; +} + +static float LoadF32(const uint8_t* map) +{ + float value; + memcpy(&value, map, sizeof(value)); + return value; +} + +static void StoreU16(uint8_t* map, uint16_t value) +{ + memcpy(map, &value, sizeof(value)); +} + +static void StoreU32(uint8_t* map, uint32_t value) +{ + memcpy(map, &value, sizeof(value)); +} + +static void StoreF32(uint8_t* map, float value) +{ + memcpy(map, &value, sizeof(value)); +} + +static uint32_t FloatBits(float value) +{ + uint32_t bits; + memcpy(&bits, &value, sizeof(bits)); + return bits; +} + +static float BitsFloat(uint32_t bits) +{ + float value; + memcpy(&value, &bits, sizeof(value)); + return value; +} + +static float HalfToFloat(uint16_t half) +{ + uint32_t sign = ((uint32_t)half & 0x8000u) << 16; + uint32_t exponent = ((uint32_t)half >> 10) & 0x1fu; + uint32_t mantissa = (uint32_t)half & 0x3ffu; + uint32_t bits; + + if(exponent == 0u) + { + if(mantissa == 0u) + bits = sign; + else + { + int shift = 0; + while((mantissa & 0x400u) == 0u) + { + mantissa <<= 1; + ++shift; + } + mantissa &= 0x3ffu; + bits = sign | ((uint32_t)(113 - shift) << 23) | (mantissa << 13); + } + } + else if(exponent == 31u) + bits = sign | 0x7f800000u | (mantissa << 13); + else + bits = sign | ((exponent + 112u) << 23) | (mantissa << 13); + + return BitsFloat(bits); +} + +static uint16_t FloatToHalf(float value) +{ + uint32_t bits = FloatBits(value); + uint32_t sign = (bits >> 16) & 0x8000u; + uint32_t exponent = (bits >> 23) & 0xffu; + uint32_t mantissa = bits & 0x7fffffu; + int half_exponent; + + if(exponent == 255u) + { + if(mantissa == 0u) + return (uint16_t)(sign | 0x7c00u); + mantissa >>= 13; + return (uint16_t)(sign | 0x7c00u | mantissa | (mantissa == 0u)); + } + + half_exponent = (int)exponent - 127 + 15; + if(half_exponent >= 31) + return (uint16_t)(sign | 0x7c00u); + if(half_exponent <= 0) + { + uint32_t rounded; + uint32_t shift; + uint32_t remainder; + uint32_t halfway; + if(half_exponent < -10) + return (uint16_t)sign; + mantissa |= 0x800000u; + shift = (uint32_t)(14 - half_exponent); + rounded = mantissa >> shift; + remainder = mantissa & ((1u << shift) - 1u); + halfway = 1u << (shift - 1u); + if(remainder > halfway || (remainder == halfway && (rounded & 1u) != 0u)) + ++rounded; + return (uint16_t)(sign | rounded); + } + else + { + uint32_t rounded = mantissa >> 13; + uint32_t remainder = mantissa & 0x1fffu; + if(remainder > 0x1000u || (remainder == 0x1000u && (rounded & 1u) != 0u)) + { + ++rounded; + if(rounded == 0x400u) + { + rounded = 0u; + ++half_exponent; + if(half_exponent >= 31) + return (uint16_t)(sign | 0x7c00u); + } + } + return (uint16_t)(sign | ((uint32_t)half_exponent << 10) | rounded); + } +} + +static int32_t SignedBits(uint32_t value, unsigned bits) +{ + uint32_t sign = 1u << (bits - 1u); + return (int32_t)((value ^ sign) - sign); +} + +static float NormalizedI8(uint8_t value) +{ + int32_t signed_value = (int32_t)(int8_t)value; + float result = (float)signed_value / 127.0f; + return result < -1.0f ? -1.0f : result; +} + +static float NormalizedI16(uint16_t value) +{ + int32_t signed_value = (int32_t)(int16_t)value; + float result = (float)signed_value / 32767.0f; + return result < -1.0f ? -1.0f : result; +} + +static float NormalizedSignedBits(uint32_t value, unsigned bits) +{ + float result = (float)SignedBits(value, bits) / (float)((1u << (bits - 1u)) - 1u); + return result < -1.0f ? -1.0f : result; +} + +static uint32_t SignExtend8(uint8_t value) +{ + return (uint32_t)(int32_t)(int8_t)value; +} + +static uint32_t SignExtend16(uint16_t value) +{ + return (uint32_t)(int32_t)(int16_t)value; +} + +static float Clamp(float value, float low, float high) +{ + if(value < low) + return low; + if(value > high) + return high; + return value; +} + +static uint32_t RoundUnsigned(float value) +{ + return (uint32_t)roundf(value); +} + +static int32_t RoundSigned(float value) +{ + return (int32_t)roundf(value); +} + +static float DecodeUFloat(uint32_t value, unsigned mantissa_bits) +{ + uint32_t mantissa_mask = (1u << mantissa_bits) - 1u; + uint32_t mantissa = value & mantissa_mask; + uint32_t exponent = (value >> mantissa_bits) & 0x1fu; + + if(exponent == 0u) + return mantissa == 0u ? 0.0f : ldexpf((float)mantissa / (float)(1u << mantissa_bits), -14); + if(exponent == 31u) + return mantissa == 0u ? INFINITY : NAN; + return ldexpf(1.0f + (float)mantissa / (float)(1u << mantissa_bits), (int)exponent - 15); +} + +static uint32_t EncodeUFloat(float value, unsigned mantissa_bits) +{ + const uint32_t max_exponent = 31u; + int exponent; + int adjusted_exponent; + float normalized; + uint32_t mantissa; + uint32_t exp_bits; + + if(isnan(value)) + return (max_exponent << mantissa_bits) | 1u; + if(isinf(value)) + return max_exponent << mantissa_bits; + if(value <= 0.0f) + return 0u; + + normalized = frexpf(value, &exponent); + adjusted_exponent = exponent - 1 + 15; + if(adjusted_exponent >= 31) + return max_exponent << mantissa_bits; + if(adjusted_exponent <= 0) + return RoundUnsigned(value * (float)(1u << (mantissa_bits + 14u))); + + mantissa = RoundUnsigned((normalized * 2.0f - 1.0f) * (float)(1u << mantissa_bits)); + exp_bits = (uint32_t)adjusted_exponent; + if(mantissa == (1u << mantissa_bits)) + { + mantissa = 0u; + ++exp_bits; + if(exp_bits >= max_exponent) + return max_exponent << mantissa_bits; + } + return (exp_bits << mantissa_bits) | mantissa; +} + +static float ClampE5Component(float value) +{ + const float max_value = 65408.0f; + if(isnan(value) || value <= 0.0f) + return 0.0f; + if(isinf(value) || value >= max_value) + return max_value; + return value; +} + +static uint32_t EncodeE5B9G9R9(PhiBlitFloat4 color) +{ + float r = ClampE5Component(color.values[0]); + float g = ClampE5Component(color.values[1]); + float b = ClampE5Component(color.values[2]); + float maximum = fmaxf(r, fmaxf(g, b)); + float scale; + int exponent_part; + int exponent_i; + uint32_t exponent; + uint32_t rounded_max; + uint32_t rm; + uint32_t gm; + uint32_t bm; + + if(maximum == 0.0f) + return 0u; + (void)frexpf(maximum, &exponent_part); + exponent_i = exponent_part + 15; + if(exponent_i < 0) + exponent_i = 0; + if(exponent_i > 31) + exponent_i = 31; + exponent = (uint32_t)exponent_i; + scale = ldexpf(1.0f, exponent_i - 24); + rounded_max = RoundUnsigned(maximum / scale); + if(rounded_max > 0x1ffu && exponent < 31u) + { + ++exponent; + scale *= 2.0f; + } + rm = RoundUnsigned(r / scale); + gm = RoundUnsigned(g / scale); + bm = RoundUnsigned(b / scale); + if(rm > 0x1ffu) + rm = 0x1ffu; + if(gm > 0x1ffu) + gm = 0x1ffu; + if(bm > 0x1ffu) + bm = 0x1ffu; + return rm | (gm << 9) | (bm << 18) | (exponent << 27); +} + +static int CanReadFloat(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_USCALED: + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SRGB: + case PHI_FORMAT_R8_SSCALED: + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8_SNORM: + case PHI_FORMAT_R16_USCALED: + case PHI_FORMAT_R16_SSCALED: + case PHI_FORMAT_R16_SNORM: + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_D16_UNORM: + case PHI_FORMAT_X8_D24_UNORM_PACK32: + case PHI_FORMAT_D24_UNORM_S8_UINT: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_SRGB: + case PHI_FORMAT_R8G8B8A8_UNORM: + case PHI_FORMAT_R8G8_USCALED: + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SRGB: + case PHI_FORMAT_R8G8_SSCALED: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8_SNORM: + case PHI_FORMAT_R8G8B8_UNORM: + case PHI_FORMAT_B8G8R8_UNORM: + case PHI_FORMAT_R8G8B8A8_USCALED: + case PHI_FORMAT_R8G8B8A8_SSCALED: + case PHI_FORMAT_R8G8B8A8_SNORM: + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16_SFLOAT: + case PHI_FORMAT_R16G16_USCALED: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R16G16_SSCALED: + case PHI_FORMAT_R16G16_SNORM: + case PHI_FORMAT_R16G16_UNORM: + case PHI_FORMAT_R16G16_SFLOAT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_D32_SFLOAT: + case PHI_FORMAT_R32G32_SFLOAT: + case PHI_FORMAT_R32G32B32_SFLOAT: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_UNORM: + case PHI_FORMAT_R16G16B16A16_USCALED: + case PHI_FORMAT_R16G16B16A16_SSCALED: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SNORM: + case PHI_FORMAT_R16G16B16A16_SFLOAT: + case PHI_FORMAT_R32G32B32A32_SFLOAT: + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_S8_UINT: + case PHI_FORMAT_B8G8R8A8_SRGB: + case PHI_FORMAT_B8G8R8A8_UNORM: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + case PHI_FORMAT_A8B8G8R8_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_USCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_SNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_USCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SNORM_PACK32: + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + return 1; + default: + return 0; + } +} + +static int CanWriteFloat(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SRGB: + case PHI_FORMAT_S8_UINT: + case PHI_FORMAT_R8_SNORM: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16_SNORM: + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_D16_UNORM: + case PHI_FORMAT_R16_SFLOAT: + case PHI_FORMAT_X8_D24_UNORM_PACK32: + case PHI_FORMAT_D24_UNORM_S8_UINT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_D32_SFLOAT: + case PHI_FORMAT_R8G8_SNORM: + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SRGB: + case PHI_FORMAT_R8G8B8_UNORM: + case PHI_FORMAT_B8G8R8_UNORM: + case PHI_FORMAT_R16G16_SNORM: + case PHI_FORMAT_R16G16_UNORM: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R16G16_SFLOAT: + case PHI_FORMAT_R32G32_SFLOAT: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_UNORM: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SNORM: + case PHI_FORMAT_R16G16B16A16_SFLOAT: + case PHI_FORMAT_B8G8R8A8_SRGB: + case PHI_FORMAT_B8G8R8A8_UNORM: + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + case PHI_FORMAT_R8G8B8A8_UNORM: + case PHI_FORMAT_R8G8B8A8_SRGB: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_USCALED: + case PHI_FORMAT_R8G8B8A8_SNORM: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_R32G32B32A32_SFLOAT: + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + return 1; + default: + return 0; + } +} + +static int CanReadInt(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_S8_UINT: + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_UINT: + case PHI_FORMAT_R32G32B32_SINT: + case PHI_FORMAT_R32G32B32_UINT: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_SINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_SINT_PACK32: + return 1; + default: + return 0; + } +} + +static int CanWriteInt(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_S8_UINT: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_UINT: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_USCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + return 1; + default: + return 0; + } +} + +static int IsInteger(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_SINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_SINT_PACK32: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32G32_UINT: + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32B32_UINT: + case PHI_FORMAT_R32G32B32_SINT: + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_S8_UINT: + return 1; + default: + return 0; + } +} + +static int IsSigned(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R8_SNORM: + case PHI_FORMAT_R8_SSCALED: + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8G8_SNORM: + case PHI_FORMAT_R8G8_SSCALED: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8B8A8_SNORM: + case PHI_FORMAT_R8G8B8A8_SSCALED: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SSCALED_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A2R10G10B10_SNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SINT_PACK32: + case PHI_FORMAT_A2B10G10R10_SNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_SINT_PACK32: + case PHI_FORMAT_R16_SNORM: + case PHI_FORMAT_R16_SSCALED: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_SFLOAT: + case PHI_FORMAT_R16G16_SNORM: + case PHI_FORMAT_R16G16_SSCALED: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_SFLOAT: + case PHI_FORMAT_R16G16B16A16_SNORM: + case PHI_FORMAT_R16G16B16A16_SSCALED: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SFLOAT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_SFLOAT: + case PHI_FORMAT_R32G32B32_SINT: + case PHI_FORMAT_R32G32B32_SFLOAT: + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_SFLOAT: + case PHI_FORMAT_D32_SFLOAT: + return 1; + default: + return 0; + } +} + +static int IsFloat(PhiFormat format) +{ + switch((int)format) + { + case PHI_FORMAT_R16_SFLOAT: + case PHI_FORMAT_R16G16_SFLOAT: + case PHI_FORMAT_R16G16B16A16_SFLOAT: + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_R32G32_SFLOAT: + case PHI_FORMAT_R32G32B32_SFLOAT: + case PHI_FORMAT_R32G32B32A32_SFLOAT: + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + case PHI_FORMAT_D32_SFLOAT: + return 1; + default: + return 0; + } +} + +static int IsSrgb(PhiFormat format) +{ + return format == PHI_FORMAT_R8_SRGB || format == PHI_FORMAT_R8G8_SRGB || format == PHI_FORMAT_R8G8B8A8_SRGB || + format == PHI_FORMAT_B8G8R8A8_SRGB || format == PHI_FORMAT_A8B8G8R8_SRGB_PACK32; +} + +static int IsUnsignedComponent(PhiFormat format, unsigned component) +{ + if(IsFloat(format)) + { + if(format == PHI_FORMAT_B10G11R11_UFLOAT_PACK32 || format == PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32 || + format == PHI_FORMAT_D32_SFLOAT) + return 1; + if(format == PHI_FORMAT_R16_SFLOAT || format == PHI_FORMAT_R32_SFLOAT) + return component >= 1u; + if(format == PHI_FORMAT_R16G16_SFLOAT || format == PHI_FORMAT_R32G32_SFLOAT) + return component >= 2u; + if(format == PHI_FORMAT_R32G32B32_SFLOAT) + return component >= 3u; + return 0; + } + if(IsSigned(format)) + { + if(format == PHI_FORMAT_R8_SNORM || format == PHI_FORMAT_R8_SSCALED || format == PHI_FORMAT_R8_SINT || + format == PHI_FORMAT_R16_SNORM || format == PHI_FORMAT_R16_SSCALED || format == PHI_FORMAT_R16_SINT || + format == PHI_FORMAT_R32_SINT) + return component >= 1u; + if(format == PHI_FORMAT_R8G8_SNORM || format == PHI_FORMAT_R8G8_SSCALED || format == PHI_FORMAT_R8G8_SINT || + format == PHI_FORMAT_R16G16_SNORM || format == PHI_FORMAT_R16G16_SSCALED || format == PHI_FORMAT_R16G16_SINT || + format == PHI_FORMAT_R32G32_SINT) + return component >= 2u; + if(format == PHI_FORMAT_R32G32B32_SINT) + return component >= 3u; + return 0; + } + /* Match format.isUnsignedComponent, including its R8_USCALED component-zero behavior. */ + if(format == PHI_FORMAT_R8_USCALED) + return component >= 1u; + return 1; +} + +static float MaxElementValue(PhiFormat format) +{ + if(format == PHI_FORMAT_D16_UNORM || format == PHI_FORMAT_X8_D24_UNORM_PACK32 || format == PHI_FORMAT_D24_UNORM_S8_UINT || + format == PHI_FORMAT_D32_SFLOAT) + return 1.0f; + if(format == PHI_FORMAT_S8_UINT) + return 255.0f; + if(IsSrgb(format)) + return 1.0f; + switch((int)format) + { + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SNORM: + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SNORM: + case PHI_FORMAT_R8G8B8_UNORM: + case PHI_FORMAT_B8G8R8_UNORM: + case PHI_FORMAT_R8G8B8A8_UNORM: + case PHI_FORMAT_R8G8B8A8_SNORM: + case PHI_FORMAT_B8G8R8A8_UNORM: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_SNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_SNORM_PACK32: + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_R16_SNORM: + case PHI_FORMAT_R16G16_UNORM: + case PHI_FORMAT_R16G16_SNORM: + case PHI_FORMAT_R16G16B16A16_UNORM: + case PHI_FORMAT_R16G16B16A16_SNORM: + return 1.0f; + default: + break; + } + if(IsFloat(format)) + return (format == PHI_FORMAT_R16_SFLOAT || format == PHI_FORMAT_R16G16_SFLOAT || + format == PHI_FORMAT_R16G16B16A16_SFLOAT || format == PHI_FORMAT_B10G11R11_UFLOAT_PACK32 || + format == PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32) + ? 65504.0f + : 3.402823466e+38f; + if(format == PHI_FORMAT_R8_USCALED || format == PHI_FORMAT_R8_UINT || format == PHI_FORMAT_R8G8_USCALED || + format == PHI_FORMAT_R8G8_UINT || format == PHI_FORMAT_R8G8B8A8_USCALED || format == PHI_FORMAT_R8G8B8A8_UINT || + format == PHI_FORMAT_A8B8G8R8_USCALED_PACK32 || format == PHI_FORMAT_A8B8G8R8_UINT_PACK32) + return 255.0f; + if(format == PHI_FORMAT_R8_SSCALED || format == PHI_FORMAT_R8_SINT || format == PHI_FORMAT_R8G8_SSCALED || + format == PHI_FORMAT_R8G8_SINT || format == PHI_FORMAT_R8G8B8A8_SSCALED || format == PHI_FORMAT_R8G8B8A8_SINT || + format == PHI_FORMAT_A8B8G8R8_SSCALED_PACK32 || format == PHI_FORMAT_A8B8G8R8_SINT_PACK32) + return 127.0f; + if(format == PHI_FORMAT_R16_USCALED || format == PHI_FORMAT_R16_UINT || format == PHI_FORMAT_R16G16_USCALED || + format == PHI_FORMAT_R16G16_UINT || format == PHI_FORMAT_R16G16B16A16_USCALED || format == PHI_FORMAT_R16G16B16A16_UINT) + return 65535.0f; + if(format == PHI_FORMAT_R16_SSCALED || format == PHI_FORMAT_R16_SINT || format == PHI_FORMAT_R16G16_SSCALED || + format == PHI_FORMAT_R16G16_SINT || format == PHI_FORMAT_R16G16B16A16_SSCALED || format == PHI_FORMAT_R16G16B16A16_SINT) + return 32767.0f; + if(format == PHI_FORMAT_R32_UINT || format == PHI_FORMAT_R32G32_UINT || format == PHI_FORMAT_R32G32B32_UINT || + format == PHI_FORMAT_R32G32B32A32_UINT) + return 4294967295.0f; + if(format == PHI_FORMAT_R32_SINT || format == PHI_FORMAT_R32G32_SINT || format == PHI_FORMAT_R32G32B32_SINT || + format == PHI_FORMAT_R32G32B32A32_SINT) + return 2147483647.0f; + if(format == PHI_FORMAT_A2R10G10B10_USCALED_PACK32 || format == PHI_FORMAT_A2R10G10B10_UINT_PACK32 || + format == PHI_FORMAT_A2B10G10R10_USCALED_PACK32 || format == PHI_FORMAT_A2B10G10R10_UINT_PACK32) + return 1023.0f; + if(format == PHI_FORMAT_A2R10G10B10_SSCALED_PACK32 || format == PHI_FORMAT_A2R10G10B10_SINT_PACK32 || + format == PHI_FORMAT_A2B10G10R10_SSCALED_PACK32 || format == PHI_FORMAT_A2B10G10R10_SINT_PACK32) + return 511.0f; + return 1.0f; +} + +static float MinElementValue(PhiFormat format) +{ + if(format == PHI_FORMAT_D16_UNORM || format == PHI_FORMAT_X8_D24_UNORM_PACK32 || format == PHI_FORMAT_D24_UNORM_S8_UINT || + format == PHI_FORMAT_D32_SFLOAT || format == PHI_FORMAT_S8_UINT || IsSrgb(format)) + return 0.0f; + if(IsSigned(format)) + { + if(format == PHI_FORMAT_R8_SNORM || format == PHI_FORMAT_R8G8_SNORM || format == PHI_FORMAT_R8G8B8A8_SNORM || + format == PHI_FORMAT_A8B8G8R8_SNORM_PACK32 || format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 || + format == PHI_FORMAT_A2B10G10R10_SNORM_PACK32 || format == PHI_FORMAT_R16_SNORM || + format == PHI_FORMAT_R16G16_SNORM || format == PHI_FORMAT_R16G16B16A16_SNORM) + return -1.0f; + if(IsFloat(format)) + return -MaxElementValue(format); + if(MaxElementValue(format) == 127.0f) + return -128.0f; + if(MaxElementValue(format) == 32767.0f) + return -32768.0f; + if(MaxElementValue(format) == 511.0f) + return -512.0f; + return -2147483648.0f; + } + return 0.0f; +} + +static void ScaleForFormat(PhiFormat format, float scale[4]) +{ + unsigned i; + for(i = 0; i < 4; ++i) + scale[i] = 1.0f; + if(format == PHI_FORMAT_R4G4B4A4_UNORM_PACK16 || format == PHI_FORMAT_B4G4R4A4_UNORM_PACK16 || + format == PHI_FORMAT_A4R4G4B4_UNORM_PACK16 || format == PHI_FORMAT_A4B4G4R4_UNORM_PACK16) + for(i = 0; i < 4; ++i) + scale[i] = 15.0f; + else if(format == PHI_FORMAT_R8_UNORM || format == PHI_FORMAT_R8G8_UNORM || format == PHI_FORMAT_R8G8B8_UNORM || + format == PHI_FORMAT_B8G8R8_UNORM || format == PHI_FORMAT_R8G8B8A8_UNORM || format == PHI_FORMAT_B8G8R8A8_UNORM || + format == PHI_FORMAT_A8B8G8R8_UNORM_PACK32 || IsSrgb(format)) + for(i = 0; i < 4; ++i) + scale[i] = 255.0f; + else if(format == PHI_FORMAT_R8_SNORM || format == PHI_FORMAT_R8G8_SNORM || format == PHI_FORMAT_R8G8B8A8_SNORM || + format == PHI_FORMAT_A8B8G8R8_SNORM_PACK32) + for(i = 0; i < 4; ++i) + scale[i] = 127.0f; + else if(format == PHI_FORMAT_R16_UNORM || format == PHI_FORMAT_R16G16_UNORM || format == PHI_FORMAT_R16G16B16A16_UNORM) + for(i = 0; i < 4; ++i) + scale[i] = 65535.0f; + else if(format == PHI_FORMAT_R16_SNORM || format == PHI_FORMAT_R16G16_SNORM || format == PHI_FORMAT_R16G16B16A16_SNORM) + for(i = 0; i < 4; ++i) + scale[i] = 32767.0f; + else if(format == PHI_FORMAT_R5G6B5_UNORM_PACK16 || format == PHI_FORMAT_B5G6R5_UNORM_PACK16) + { + scale[0] = 31.0f; + scale[1] = 63.0f; + scale[2] = 31.0f; + } + else if(format == PHI_FORMAT_R5G5B5A1_UNORM_PACK16 || format == PHI_FORMAT_B5G5R5A1_UNORM_PACK16 || + format == PHI_FORMAT_A1R5G5B5_UNORM_PACK16) + { + scale[0] = scale[1] = scale[2] = 31.0f; + } + else if(format == PHI_FORMAT_A2R10G10B10_UNORM_PACK32 || format == PHI_FORMAT_A2B10G10R10_UNORM_PACK32) + { + scale[0] = scale[1] = scale[2] = 1023.0f; + scale[3] = 3.0f; + } + else if(format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 || format == PHI_FORMAT_A2B10G10R10_SNORM_PACK32) + { + scale[0] = scale[1] = scale[2] = 511.0f; + } + else if(format == PHI_FORMAT_D16_UNORM) + { + scale[0] = 65535.0f; + scale[1] = scale[2] = scale[3] = 0.0f; + } + else if(format == PHI_FORMAT_X8_D24_UNORM_PACK32 || format == PHI_FORMAT_D24_UNORM_S8_UINT) + { + scale[0] = 16777215.0f; + scale[1] = scale[2] = scale[3] = 0.0f; + } +} + +int PhiGetBlitFormatInfo(PhiFormat format, PhiBlitFormatInfo* info) +{ + uint32_t size; + if(info == NULL) + return 0; + switch((int)format) + { + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SNORM: + case PHI_FORMAT_R8_USCALED: + case PHI_FORMAT_R8_SSCALED: + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8_SRGB: + case PHI_FORMAT_S8_UINT: + size = 1; + break; + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_R16_SNORM: + case PHI_FORMAT_R16_USCALED: + case PHI_FORMAT_R16_SSCALED: + case PHI_FORMAT_R16_UINT: + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_SFLOAT: + case PHI_FORMAT_D16_UNORM: + size = 2; + break; + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SNORM: + case PHI_FORMAT_R8G8_USCALED: + case PHI_FORMAT_R8G8_SSCALED: + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8_SRGB: + size = 2; + break; + case PHI_FORMAT_R8G8B8_UNORM: + case PHI_FORMAT_B8G8R8_UNORM: + size = 3; + break; + case PHI_FORMAT_R8G8B8A8_UNORM: + case PHI_FORMAT_R8G8B8A8_SNORM: + case PHI_FORMAT_R8G8B8A8_USCALED: + case PHI_FORMAT_R8G8B8A8_SSCALED: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R8G8B8A8_SRGB: + case PHI_FORMAT_B8G8R8A8_UNORM: + case PHI_FORMAT_B8G8R8A8_SRGB: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + case PHI_FORMAT_A8B8G8R8_SSCALED_PACK32: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_SNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_USCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_SINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_SNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_USCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_SINT_PACK32: + case PHI_FORMAT_R16G16_UNORM: + case PHI_FORMAT_R16G16_SNORM: + case PHI_FORMAT_R16G16_USCALED: + case PHI_FORMAT_R16G16_SSCALED: + case PHI_FORMAT_R16G16_UINT: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_SFLOAT: + case PHI_FORMAT_R32_UINT: + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + case PHI_FORMAT_X8_D24_UNORM_PACK32: + case PHI_FORMAT_D24_UNORM_S8_UINT: + case PHI_FORMAT_D32_SFLOAT: + size = 4; + break; + case PHI_FORMAT_R16G16B16A16_UNORM: + case PHI_FORMAT_R16G16B16A16_SNORM: + case PHI_FORMAT_R16G16B16A16_USCALED: + case PHI_FORMAT_R16G16B16A16_SSCALED: + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SFLOAT: + case PHI_FORMAT_R32G32_UINT: + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_SFLOAT: + size = 8; + break; + case PHI_FORMAT_R32G32B32_UINT: + case PHI_FORMAT_R32G32B32_SINT: + case PHI_FORMAT_R32G32B32_SFLOAT: + size = 12; + break; + case PHI_FORMAT_R32G32B32A32_UINT: + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_SFLOAT: + size = 16; + break; + default: + return 0; + } + memset(info, 0, sizeof(*info)); + info->texel_size = size; + info->is_integer = (uint8_t)IsInteger(format); + info->is_signed = (uint8_t)IsSigned(format); + info->is_float = (uint8_t)IsFloat(format); + info->is_srgb = (uint8_t)IsSrgb(format); + info->is_unsigned = (uint8_t)IsUnsignedComponent(format, 0u); + info->can_read_float = (uint8_t)CanReadFloat(format); + info->can_write_float = (uint8_t)CanWriteFloat(format); + info->can_read_int = (uint8_t)CanReadInt(format); + info->can_write_int = (uint8_t)CanWriteInt(format); + info->vector_unorm8x4 = (uint8_t)(format == PHI_FORMAT_R8G8B8A8_UNORM || format == PHI_FORMAT_B8G8R8A8_UNORM || + format == PHI_FORMAT_A8B8G8R8_UNORM_PACK32); + return 1; +} + +PhiBlitFloat4 PhiReadBlitFloat4(const uint8_t* map, PhiFormat format) +{ + PhiBlitFloat4 c = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + uint32_t pack; + uint16_t value; + unsigned i; + if(map == NULL || !CanReadFloat(format)) + return c; + switch((int)format) + { + case PHI_FORMAT_R8_USCALED: + c.values[0] = (float)map[0]; + break; + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SRGB: + c.values[0] = (float)map[0] / 255.0f; + break; + case PHI_FORMAT_R8_SSCALED: + c.values[0] = (float)(int8_t)map[0]; + break; + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8_SNORM: + c.values[0] = NormalizedI8(map[0]); + break; + case PHI_FORMAT_R16_USCALED: + c.values[0] = (float)LoadU16(map); + break; + case PHI_FORMAT_R16_SSCALED: + c.values[0] = (float)(int16_t)LoadU16(map); + break; + case PHI_FORMAT_R16_SNORM: + c.values[0] = NormalizedI16(LoadU16(map)); + break; + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_D16_UNORM: + c.values[0] = (float)LoadU16(map) / 65535.0f; + break; + case PHI_FORMAT_X8_D24_UNORM_PACK32: + case PHI_FORMAT_D24_UNORM_S8_UINT: + c.values[0] = (float)(LoadU32(map) & 0xffffffu) / 16777215.0f; + break; + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_SRGB: + case PHI_FORMAT_R8G8B8A8_UNORM: + for(i = 0; i < 4; ++i) + c.values[i] = (float)map[i] / 255.0f; + break; + case PHI_FORMAT_R8G8_USCALED: + c.values[0] = (float)map[0]; + c.values[1] = (float)map[1]; + break; + case PHI_FORMAT_R8G8_UINT: + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SRGB: + c.values[0] = (float)map[0] / 255.0f; + c.values[1] = (float)map[1] / 255.0f; + break; + case PHI_FORMAT_R8G8_SSCALED: + c.values[0] = (float)(int8_t)map[0]; + c.values[1] = (float)(int8_t)map[1]; + break; + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8_SNORM: + c.values[0] = NormalizedI8(map[0]); + c.values[1] = NormalizedI8(map[1]); + break; + case PHI_FORMAT_R8G8B8_UNORM: + c.values[0] = (float)map[0] / 255.0f; + c.values[1] = (float)map[1] / 255.0f; + c.values[2] = (float)map[2] / 255.0f; + break; + case PHI_FORMAT_B8G8R8_UNORM: + c.values[0] = (float)map[2] / 255.0f; + c.values[1] = (float)map[1] / 255.0f; + c.values[2] = (float)map[0] / 255.0f; + break; + case PHI_FORMAT_R8G8B8A8_USCALED: + for(i = 0; i < 4; ++i) + c.values[i] = (float)map[i]; + break; + case PHI_FORMAT_R8G8B8A8_SSCALED: + for(i = 0; i < 4; ++i) + c.values[i] = (float)(int8_t)map[i]; + break; + case PHI_FORMAT_R8G8B8A8_SNORM: + for(i = 0; i < 4; ++i) + c.values[i] = NormalizedI8(map[i]); + break; + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + value = LoadU16(map); + if(format == PHI_FORMAT_R4G4B4A4_UNORM_PACK16) + { + c.values[0] = (float)((value >> 12) & 15u) / 15.0f; + c.values[1] = (float)((value >> 8) & 15u) / 15.0f; + c.values[2] = (float)((value >> 4) & 15u) / 15.0f; + c.values[3] = (float)(value & 15u) / 15.0f; + } + else if(format == PHI_FORMAT_B4G4R4A4_UNORM_PACK16) + { + c.values[2] = (float)((value >> 12) & 15u) / 15.0f; + c.values[1] = (float)((value >> 8) & 15u) / 15.0f; + c.values[0] = (float)((value >> 4) & 15u) / 15.0f; + c.values[3] = (float)(value & 15u) / 15.0f; + } + else if(format == PHI_FORMAT_A4R4G4B4_UNORM_PACK16) + { + c.values[3] = (float)((value >> 12) & 15u) / 15.0f; + c.values[0] = (float)((value >> 8) & 15u) / 15.0f; + c.values[1] = (float)((value >> 4) & 15u) / 15.0f; + c.values[2] = (float)(value & 15u) / 15.0f; + } + else + { + c.values[3] = (float)((value >> 12) & 15u) / 15.0f; + c.values[2] = (float)((value >> 8) & 15u) / 15.0f; + c.values[1] = (float)((value >> 4) & 15u) / 15.0f; + c.values[0] = (float)(value & 15u) / 15.0f; + } + break; + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + c.values[0] = (float)LoadU16(map); + break; + case PHI_FORMAT_R16_SFLOAT: + c.values[0] = HalfToFloat(LoadU16(map)); + break; + case PHI_FORMAT_R16G16_USCALED: + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_UINT: + c.values[0] = (float)LoadU16(map); + c.values[1] = (float)LoadU16(map + 2); + break; + case PHI_FORMAT_R16G16_SSCALED: + c.values[0] = (float)(int16_t)LoadU16(map); + c.values[1] = (float)(int16_t)LoadU16(map + 2); + break; + case PHI_FORMAT_R16G16_SNORM: + c.values[0] = NormalizedI16(LoadU16(map)); + c.values[1] = NormalizedI16(LoadU16(map + 2)); + break; + case PHI_FORMAT_R16G16_UNORM: + c.values[0] = (float)LoadU16(map) / 65535.0f; + c.values[1] = (float)LoadU16(map + 2) / 65535.0f; + break; + case PHI_FORMAT_R16G16_SFLOAT: + c.values[0] = HalfToFloat(LoadU16(map)); + c.values[1] = HalfToFloat(LoadU16(map + 2)); + break; + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + c.values[0] = (float)LoadU32(map); + break; + case PHI_FORMAT_R32G32_SFLOAT: + c.values[0] = LoadF32(map); + c.values[1] = LoadF32(map + 4); + break; + case PHI_FORMAT_R32G32B32_SFLOAT: + c.values[0] = LoadF32(map); + c.values[1] = LoadF32(map + 4); + c.values[2] = LoadF32(map + 8); + break; + case PHI_FORMAT_D32_SFLOAT: + case PHI_FORMAT_R32_SFLOAT: + c.values[0] = LoadF32(map); + break; + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_UNORM: + for(i = 0; i < 4; ++i) + c.values[i] = (float)LoadU16(map + 2 * i) / 65535.0f; + break; + case PHI_FORMAT_R16G16B16A16_USCALED: + for(i = 0; i < 4; ++i) + c.values[i] = (float)LoadU16(map + 2 * i); + break; + case PHI_FORMAT_R16G16B16A16_SSCALED: + for(i = 0; i < 4; ++i) + c.values[i] = (float)(int16_t)LoadU16(map + 2 * i); + break; + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SNORM: + for(i = 0; i < 4; ++i) + c.values[i] = NormalizedI16(LoadU16(map + 2 * i)); + break; + case PHI_FORMAT_R16G16B16A16_SFLOAT: + for(i = 0; i < 4; ++i) + c.values[i] = HalfToFloat(LoadU16(map + 2 * i)); + break; + case PHI_FORMAT_R32G32B32A32_SFLOAT: + for(i = 0; i < 4; ++i) + c.values[i] = LoadF32(map + 4 * i); + break; + case PHI_FORMAT_R32G32B32A32_UINT: + for(i = 0; i < 4; ++i) + c.values[i] = (float)LoadU32(map + 4 * i); + break; + case PHI_FORMAT_S8_UINT: + c.values[0] = (float)map[0]; + break; + case PHI_FORMAT_B8G8R8A8_SRGB: + case PHI_FORMAT_B8G8R8A8_UNORM: + c.values[0] = (float)map[2] / 255.0f; + c.values[1] = (float)map[1] / 255.0f; + c.values[2] = (float)map[0] / 255.0f; + c.values[3] = (float)map[3] / 255.0f; + break; + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = (float)map[i] / 255.0f; + break; + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = NormalizedI8(map[i]); + break; + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = (float)map[i]; + break; + case PHI_FORMAT_A8B8G8R8_SSCALED_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = (float)(int8_t)map[i]; + break; + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + pack = LoadU32(map); + c.values[0] = (float)(pack & 1023u) / 1023.0f; + c.values[1] = (float)((pack >> 10) & 1023u) / 1023.0f; + c.values[2] = (float)((pack >> 20) & 1023u) / 1023.0f; + c.values[3] = (float)(pack >> 30) / 3.0f; + break; + case PHI_FORMAT_A2B10G10R10_USCALED_PACK32: + pack = LoadU32(map); + c.values[0] = (float)(pack & 1023u); + c.values[1] = (float)((pack >> 10) & 1023u); + c.values[2] = (float)((pack >> 20) & 1023u); + c.values[3] = (float)(pack >> 30); + break; + case PHI_FORMAT_A2B10G10R10_SSCALED_PACK32: + case PHI_FORMAT_A2B10G10R10_SNORM_PACK32: + pack = LoadU32(map); + for(i = 0; i < 3; ++i) + c.values[i] = format == PHI_FORMAT_A2B10G10R10_SNORM_PACK32 + ? NormalizedSignedBits((pack >> (10 * i)) & 1023u, 10) + : (float)SignedBits((pack >> (10 * i)) & 1023u, 10); + c.values[3] = format == PHI_FORMAT_A2B10G10R10_SNORM_PACK32 ? NormalizedSignedBits(pack >> 30, 2) + : (float)SignedBits(pack >> 30, 2); + break; + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + pack = LoadU32(map); + c.values[2] = (float)(pack & 1023u) / 1023.0f; + c.values[1] = (float)((pack >> 10) & 1023u) / 1023.0f; + c.values[0] = (float)((pack >> 20) & 1023u) / 1023.0f; + c.values[3] = (float)(pack >> 30) / 3.0f; + break; + case PHI_FORMAT_A2R10G10B10_USCALED_PACK32: + pack = LoadU32(map); + c.values[2] = (float)(pack & 1023u); + c.values[1] = (float)((pack >> 10) & 1023u); + c.values[0] = (float)((pack >> 20) & 1023u); + c.values[3] = (float)(pack >> 30); + break; + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SNORM_PACK32: + pack = LoadU32(map); + c.values[2] = format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 ? NormalizedSignedBits(pack & 1023u, 10) + : (float)SignedBits(pack & 1023u, 10); + c.values[1] = format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 ? NormalizedSignedBits((pack >> 10) & 1023u, 10) + : (float)SignedBits((pack >> 10) & 1023u, 10); + c.values[0] = format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 ? NormalizedSignedBits((pack >> 20) & 1023u, 10) + : (float)SignedBits((pack >> 20) & 1023u, 10); + c.values[3] = format == PHI_FORMAT_A2R10G10B10_SNORM_PACK32 ? NormalizedSignedBits(pack >> 30, 2) + : (float)SignedBits(pack >> 30, 2); + break; + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + value = LoadU16(map); + c.values[1] = (float)((value >> 5) & 63u) / 63.0f; + if(format == PHI_FORMAT_R5G6B5_UNORM_PACK16) + { + c.values[0] = (float)(value >> 11) / 31.0f; + c.values[2] = (float)(value & 31u) / 31.0f; + } + else + { + c.values[2] = (float)(value >> 11) / 31.0f; + c.values[0] = (float)(value & 31u) / 31.0f; + } + break; + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + value = LoadU16(map); + c.values[1] = (float)((value >> 6) & 31u) / 31.0f; + c.values[3] = (float)(value & 1u); + if(format == PHI_FORMAT_R5G5B5A1_UNORM_PACK16) + { + c.values[0] = (float)(value >> 11) / 31.0f; + c.values[2] = (float)((value >> 1) & 31u) / 31.0f; + } + else + { + c.values[2] = (float)(value >> 11) / 31.0f; + c.values[0] = (float)((value >> 1) & 31u) / 31.0f; + } + break; + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + value = LoadU16(map); + c.values[0] = (float)((value >> 10) & 31u) / 31.0f; + c.values[1] = (float)((value >> 5) & 31u) / 31.0f; + c.values[2] = (float)(value & 31u) / 31.0f; + c.values[3] = (float)(value >> 15); + break; + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + pack = LoadU32(map); + c.values[0] = DecodeUFloat(pack & 0x7ffu, 6); + c.values[1] = DecodeUFloat((pack >> 11) & 0x7ffu, 6); + c.values[2] = DecodeUFloat(pack >> 22, 5); + break; + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + pack = LoadU32(map); + c.values[0] = (float)(pack & 0x1ffu) * ldexpf(1.0f, (int)(pack >> 27) - 24); + c.values[1] = (float)((pack >> 9) & 0x1ffu) * ldexpf(1.0f, (int)(pack >> 27) - 24); + c.values[2] = (float)((pack >> 18) & 0x1ffu) * ldexpf(1.0f, (int)(pack >> 27) - 24); + break; + default: + break; + } + return c; +} + +void PhiWriteBlitFloat4(PhiBlitFloat4 input, uint8_t* map, PhiFormat format) +{ + PhiBlitFloat4 c = input; + float low; + float high; + uint32_t pack; + uint32_t r, g, b, a; + unsigned i; + if(map == NULL || !CanWriteFloat(format)) + return; + low = MinElementValue(format); + high = MaxElementValue(format); + for(i = 0; i < 4; ++i) + c.values[i] = Clamp(c.values[i], low, high); + switch((int)format) + { + case PHI_FORMAT_R8_UNORM: + case PHI_FORMAT_R8_SRGB: + case PHI_FORMAT_S8_UINT: + map[0] = (uint8_t)RoundUnsigned(c.values[0] * 255.0f); + break; + case PHI_FORMAT_R8_SNORM: + map[0] = (uint8_t)RoundSigned(c.values[0] * 127.0f); + break; + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + StoreU16(map, (uint16_t)RoundUnsigned(c.values[0])); + break; + case PHI_FORMAT_R16_SNORM: + StoreU16(map, (uint16_t)RoundSigned(c.values[0] * 32767.0f)); + break; + case PHI_FORMAT_R16_UNORM: + case PHI_FORMAT_D16_UNORM: + StoreU16(map, (uint16_t)RoundUnsigned(c.values[0] * 65535.0f)); + break; + case PHI_FORMAT_X8_D24_UNORM_PACK32: + case PHI_FORMAT_D24_UNORM_S8_UINT: + pack = (LoadU32(map) & 0xff000000u) | RoundUnsigned(c.values[0] * 16777215.0f); + StoreU32(map, pack); + break; + case PHI_FORMAT_R16_SFLOAT: + StoreU16(map, FloatToHalf(c.values[0])); + break; + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + StoreU32(map, RoundUnsigned(c.values[0])); + break; + case PHI_FORMAT_R32_SFLOAT: + case PHI_FORMAT_D32_SFLOAT: + StoreF32(map, c.values[0]); + break; + case PHI_FORMAT_R8G8_SNORM: + for(i = 0; i < 2; ++i) + map[i] = (uint8_t)RoundSigned(c.values[i] * 127.0f); + break; + case PHI_FORMAT_R8G8_UNORM: + case PHI_FORMAT_R8G8_SRGB: + for(i = 0; i < 2; ++i) + map[i] = (uint8_t)RoundUnsigned(c.values[i] * 255.0f); + break; + case PHI_FORMAT_R16G16_SNORM: + for(i = 0; i < 2; ++i) + StoreU16(map + 2 * i, (uint16_t)RoundSigned(c.values[i] * 32767.0f)); + break; + case PHI_FORMAT_R16G16_UNORM: + for(i = 0; i < 2; ++i) + StoreU16(map + 2 * i, (uint16_t)RoundUnsigned(c.values[i] * 65535.0f)); + break; + case PHI_FORMAT_R16G16_UINT: + for(i = 0; i < 2; ++i) + StoreU16(map + 2 * i, (uint16_t)RoundUnsigned(c.values[i])); + break; + case PHI_FORMAT_R16G16_SFLOAT: + for(i = 0; i < 2; ++i) + StoreU16(map + 2 * i, FloatToHalf(c.values[i])); + break; + case PHI_FORMAT_R32G32_SFLOAT: + for(i = 0; i < 2; ++i) + StoreF32(map + 4 * i, c.values[i]); + break; + case PHI_FORMAT_R16G16B16A16_UINT: + case PHI_FORMAT_R16G16B16A16_UNORM: + for(i = 0; i < 4; ++i) + StoreU16(map + 2 * i, (uint16_t)RoundUnsigned(c.values[i] * 65535.0f)); + break; + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_SNORM: + for(i = 0; i < 4; ++i) + StoreU16(map + 2 * i, (uint16_t)RoundSigned(c.values[i] * 32767.0f)); + break; + case PHI_FORMAT_R16G16B16A16_SFLOAT: + for(i = 0; i < 4; ++i) + StoreU16(map + 2 * i, FloatToHalf(c.values[i])); + break; + case PHI_FORMAT_R8G8B8_UNORM: + map[0] = (uint8_t)RoundUnsigned(c.values[0] * 255.0f); + map[1] = (uint8_t)RoundUnsigned(c.values[1] * 255.0f); + map[2] = (uint8_t)RoundUnsigned(c.values[2] * 255.0f); + break; + case PHI_FORMAT_B8G8R8_UNORM: + map[0] = (uint8_t)RoundUnsigned(c.values[2] * 255.0f); + map[1] = (uint8_t)RoundUnsigned(c.values[1] * 255.0f); + map[2] = (uint8_t)RoundUnsigned(c.values[0] * 255.0f); + break; + case PHI_FORMAT_B8G8R8A8_SRGB: + case PHI_FORMAT_B8G8R8A8_UNORM: + map[0] = (uint8_t)RoundUnsigned(c.values[2] * 255.0f); + map[1] = (uint8_t)RoundUnsigned(c.values[1] * 255.0f); + map[2] = (uint8_t)RoundUnsigned(c.values[0] * 255.0f); + map[3] = (uint8_t)RoundUnsigned(c.values[3] * 255.0f); + break; + case PHI_FORMAT_R4G4B4A4_UNORM_PACK16: + case PHI_FORMAT_B4G4R4A4_UNORM_PACK16: + case PHI_FORMAT_A4R4G4B4_UNORM_PACK16: + case PHI_FORMAT_A4B4G4R4_UNORM_PACK16: + r = RoundUnsigned(c.values[0] * 15.0f); + g = RoundUnsigned(c.values[1] * 15.0f); + b = RoundUnsigned(c.values[2] * 15.0f); + a = RoundUnsigned(c.values[3] * 15.0f); + if(format == PHI_FORMAT_R4G4B4A4_UNORM_PACK16) + pack = (r << 12) | (g << 8) | (b << 4) | a; + else if(format == PHI_FORMAT_B4G4R4A4_UNORM_PACK16) + pack = (b << 12) | (g << 8) | (r << 4) | a; + else if(format == PHI_FORMAT_A4R4G4B4_UNORM_PACK16) + pack = (a << 12) | (r << 8) | (g << 4) | b; + else + pack = (a << 12) | (b << 8) | (g << 4) | r; + StoreU16(map, (uint16_t)pack); + break; + case PHI_FORMAT_R8G8B8A8_UNORM: + case PHI_FORMAT_R8G8B8A8_SRGB: + case PHI_FORMAT_R8G8B8A8_UINT: + case PHI_FORMAT_R8G8B8A8_USCALED: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + for(i = 0; i < 4; ++i) + map[i] = (uint8_t)RoundUnsigned(c.values[i] * 255.0f); + break; + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + case PHI_FORMAT_R8G8B8A8_SNORM: + for(i = 0; i < 4; ++i) + map[i] = (uint8_t)RoundSigned(c.values[i] * 127.0f); + break; + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + r = RoundUnsigned(c.values[0] * 1023.0f); + g = RoundUnsigned(c.values[1] * 1023.0f); + b = RoundUnsigned(c.values[2] * 1023.0f); + a = RoundUnsigned(c.values[3] * 3.0f); + StoreU32(map, (b) | (g << 10) | (r << 20) | (a << 30)); + break; + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + r = RoundUnsigned(c.values[0] * 1023.0f); + g = RoundUnsigned(c.values[1] * 1023.0f); + b = RoundUnsigned(c.values[2] * 1023.0f); + a = RoundUnsigned(c.values[3] * 3.0f); + StoreU32(map, r | (g << 10) | (b << 20) | (a << 30)); + break; + case PHI_FORMAT_R32G32B32A32_UINT: + for(i = 0; i < 4; ++i) + StoreU32(map + 4 * i, RoundUnsigned(c.values[i])); + break; + case PHI_FORMAT_R32G32B32A32_SFLOAT: + for(i = 0; i < 4; ++i) + StoreF32(map + 4 * i, c.values[i]); + break; + case PHI_FORMAT_R5G6B5_UNORM_PACK16: + case PHI_FORMAT_B5G6R5_UNORM_PACK16: + r = RoundUnsigned(c.values[0] * 31.0f); + g = RoundUnsigned(c.values[1] * 63.0f); + b = RoundUnsigned(c.values[2] * 31.0f); + StoreU16( + map, + (uint16_t)(format == PHI_FORMAT_R5G6B5_UNORM_PACK16 ? (r << 11) | (g << 5) | b : (b << 11) | (g << 5) | r)); + break; + case PHI_FORMAT_R5G5B5A1_UNORM_PACK16: + case PHI_FORMAT_B5G5R5A1_UNORM_PACK16: + r = RoundUnsigned(c.values[0] * 31.0f); + g = RoundUnsigned(c.values[1] * 31.0f); + b = RoundUnsigned(c.values[2] * 31.0f); + a = RoundUnsigned(c.values[3]); + StoreU16(map, + (uint16_t)(format == PHI_FORMAT_R5G5B5A1_UNORM_PACK16 ? (r << 11) | (g << 6) | (b << 1) | a + : (b << 11) | (g << 6) | (r << 1) | a)); + break; + case PHI_FORMAT_A1R5G5B5_UNORM_PACK16: + r = RoundUnsigned(c.values[0] * 31.0f); + g = RoundUnsigned(c.values[1] * 31.0f); + b = RoundUnsigned(c.values[2] * 31.0f); + a = RoundUnsigned(c.values[3]); + StoreU16(map, (uint16_t)(b | (g << 5) | (r << 10) | (a << 15))); + break; + case PHI_FORMAT_B10G11R11_UFLOAT_PACK32: + StoreU32(map, + EncodeUFloat(c.values[0], 6) | (EncodeUFloat(c.values[1], 6) << 11) | + (EncodeUFloat(c.values[2], 5) << 22)); + break; + case PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32: + StoreU32(map, EncodeE5B9G9R9(c)); + break; + default: + break; + } +} + +PhiBlitInt4 PhiReadBlitInt4(const uint8_t* map, PhiFormat format) +{ + PhiBlitInt4 c = { { 0u, 0u, 0u, 1u } }; + uint32_t pack; + unsigned i; + if(map == NULL || !CanReadInt(format)) + return c; + switch((int)format) + { + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_S8_UINT: + c.values[0] = map[0]; + break; + case PHI_FORMAT_R8_SINT: + c.values[0] = SignExtend8(map[0]); + break; + case PHI_FORMAT_R16_UINT: + c.values[0] = LoadU16(map); + break; + case PHI_FORMAT_R16_SINT: + c.values[0] = SignExtend16(LoadU16(map)); + break; + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + c.values[0] = LoadU32(map); + break; + case PHI_FORMAT_R8G8_UINT: + for(i = 0; i < 2; ++i) + c.values[i] = map[i]; + break; + case PHI_FORMAT_R8G8_SINT: + for(i = 0; i < 2; ++i) + c.values[i] = SignExtend8(map[i]); + break; + case PHI_FORMAT_R16G16_UINT: + for(i = 0; i < 2; ++i) + c.values[i] = LoadU16(map + 2 * i); + break; + case PHI_FORMAT_R16G16_SINT: + for(i = 0; i < 2; ++i) + c.values[i] = SignExtend16(LoadU16(map + 2 * i)); + break; + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_UINT: + for(i = 0; i < 2; ++i) + c.values[i] = LoadU32(map + 4 * i); + break; + case PHI_FORMAT_R32G32B32_SINT: + case PHI_FORMAT_R32G32B32_UINT: + for(i = 0; i < 3; ++i) + c.values[i] = LoadU32(map + 4 * i); + break; + case PHI_FORMAT_R8G8B8A8_UINT: + for(i = 0; i < 4; ++i) + c.values[i] = map[i]; + break; + case PHI_FORMAT_R8G8B8A8_SINT: + for(i = 0; i < 4; ++i) + c.values[i] = SignExtend8(map[i]); + break; + case PHI_FORMAT_R16G16B16A16_UINT: + for(i = 0; i < 4; ++i) + c.values[i] = LoadU16(map + 2 * i); + break; + case PHI_FORMAT_R16G16B16A16_SINT: + for(i = 0; i < 4; ++i) + c.values[i] = SignExtend16(LoadU16(map + 2 * i)); + break; + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_UINT: + for(i = 0; i < 4; ++i) + c.values[i] = LoadU32(map + 4 * i); + break; + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SNORM_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = map[i]; + break; + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + for(i = 0; i < 4; ++i) + c.values[i] = SignExtend8(map[i]); + break; + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + pack = LoadU32(map); + c.values[0] = pack & 1023u; + c.values[1] = (pack >> 10) & 1023u; + c.values[2] = (pack >> 20) & 1023u; + c.values[3] = pack >> 30; + break; + case PHI_FORMAT_A2B10G10R10_SINT_PACK32: + pack = LoadU32(map); + c.values[0] = (uint32_t)SignedBits(pack & 1023u, 10); + c.values[1] = (uint32_t)SignedBits((pack >> 10) & 1023u, 10); + c.values[2] = (uint32_t)SignedBits((pack >> 20) & 1023u, 10); + c.values[3] = (uint32_t)SignedBits(pack >> 30, 2); + break; + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + pack = LoadU32(map); + c.values[2] = pack & 1023u; + c.values[1] = (pack >> 10) & 1023u; + c.values[0] = (pack >> 20) & 1023u; + c.values[3] = pack >> 30; + break; + case PHI_FORMAT_A2R10G10B10_SINT_PACK32: + pack = LoadU32(map); + c.values[2] = (uint32_t)SignedBits(pack & 1023u, 10); + c.values[1] = (uint32_t)SignedBits((pack >> 10) & 1023u, 10); + c.values[0] = (uint32_t)SignedBits((pack >> 20) & 1023u, 10); + c.values[3] = (uint32_t)SignedBits(pack >> 30, 2); + break; + default: + break; + } + return c; +} + +static uint32_t ClampUnsignedBits(uint32_t value, uint32_t maximum) +{ + return value > maximum ? maximum : value; +} +static uint32_t ClampSignedBitsValue(uint32_t value, int32_t low, int32_t high) +{ + int32_t signed_value = (int32_t)value; + if(signed_value < low) + signed_value = low; + if(signed_value > high) + signed_value = high; + return (uint32_t)signed_value; +} + +void PhiWriteBlitInt4(PhiBlitInt4 input, uint8_t* map, PhiFormat format) +{ + PhiBlitInt4 c = input; + uint32_t pack; + unsigned i; + if(map == NULL || !CanWriteInt(format)) + return; + if(format == PHI_FORMAT_A2R10G10B10_UINT_PACK32 || format == PHI_FORMAT_A2B10G10R10_UINT_PACK32) + { + for(i = 0; i < 3; ++i) + c.values[i] = ClampUnsignedBits(c.values[i], 1023u); + c.values[3] = ClampUnsignedBits(c.values[3], 3u); + } + else if(format == PHI_FORMAT_A8B8G8R8_UINT_PACK32 || format == PHI_FORMAT_A8B8G8R8_USCALED_PACK32 || + format == PHI_FORMAT_R8G8B8A8_UINT || format == PHI_FORMAT_R8G8_UINT || format == PHI_FORMAT_R8_UINT) + { + for(i = 0; i < 4; ++i) + c.values[i] = ClampUnsignedBits(c.values[i], 255u); + } + else if(format == PHI_FORMAT_R16G16B16A16_UINT || format == PHI_FORMAT_R16G16_UINT || format == PHI_FORMAT_R16_UINT) + { + for(i = 0; i < 4; ++i) + c.values[i] = ClampUnsignedBits(c.values[i], 65535u); + } + else if(format == PHI_FORMAT_A8B8G8R8_SINT_PACK32 || format == PHI_FORMAT_R8G8B8A8_SINT || format == PHI_FORMAT_R8G8_SINT || + format == PHI_FORMAT_R8_SINT) + { + for(i = 0; i < 4; ++i) + c.values[i] = ClampSignedBitsValue(c.values[i], -128, 127); + } + else if(format == PHI_FORMAT_R16G16B16A16_SINT || format == PHI_FORMAT_R16G16_SINT || format == PHI_FORMAT_R16_SINT) + { + for(i = 0; i < 4; ++i) + c.values[i] = ClampSignedBitsValue(c.values[i], -32768, 32767); + } + switch((int)format) + { + case PHI_FORMAT_R8_SINT: + case PHI_FORMAT_R8_UINT: + case PHI_FORMAT_S8_UINT: + map[0] = (uint8_t)c.values[0]; + break; + case PHI_FORMAT_R8G8_SINT: + case PHI_FORMAT_R8G8_UINT: + for(i = 0; i < 2; ++i) + map[i] = (uint8_t)c.values[i]; + break; + case PHI_FORMAT_R16_SINT: + case PHI_FORMAT_R16_UINT: + StoreU16(map, (uint16_t)c.values[0]); + break; + case PHI_FORMAT_R16G16_SINT: + case PHI_FORMAT_R16G16_UINT: + for(i = 0; i < 2; ++i) + StoreU16(map + 2 * i, (uint16_t)c.values[i]); + break; + case PHI_FORMAT_R32_SINT: + case PHI_FORMAT_R32_UINT: + StoreU32(map, c.values[0]); + break; + case PHI_FORMAT_R32G32_SINT: + case PHI_FORMAT_R32G32_UINT: + for(i = 0; i < 2; ++i) + StoreU32(map + 4 * i, c.values[i]); + break; + case PHI_FORMAT_R8G8B8A8_SINT: + case PHI_FORMAT_R8G8B8A8_UINT: + for(i = 0; i < 4; ++i) + map[i] = (uint8_t)c.values[i]; + break; + case PHI_FORMAT_R16G16B16A16_SINT: + case PHI_FORMAT_R16G16B16A16_UINT: + for(i = 0; i < 4; ++i) + StoreU16(map + 2 * i, (uint16_t)c.values[i]); + break; + case PHI_FORMAT_R32G32B32A32_SINT: + case PHI_FORMAT_R32G32B32A32_UINT: + for(i = 0; i < 4; ++i) + StoreU32(map + 4 * i, c.values[i]); + break; + case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: + case PHI_FORMAT_A8B8G8R8_SINT_PACK32: + case PHI_FORMAT_A8B8G8R8_SRGB_PACK32: + case PHI_FORMAT_A8B8G8R8_UINT_PACK32: + case PHI_FORMAT_A8B8G8R8_USCALED_PACK32: + for(i = 0; i < 4; ++i) + map[i] = (uint8_t)c.values[i]; + break; + case PHI_FORMAT_A2R10G10B10_UNORM_PACK32: + case PHI_FORMAT_A2R10G10B10_UINT_PACK32: + case PHI_FORMAT_A2R10G10B10_USCALED_PACK32: + case PHI_FORMAT_A2R10G10B10_SSCALED_PACK32: + pack = (c.values[0] << 20) | (c.values[2]) | (c.values[1] << 10) | (c.values[3] << 30); + StoreU32(map, pack); + break; + case PHI_FORMAT_A2B10G10R10_UNORM_PACK32: + case PHI_FORMAT_A2B10G10R10_UINT_PACK32: + pack = (c.values[0] & 1023u) | ((c.values[1] & 1023u) << 10) | ((c.values[2] & 1023u) << 20) | + ((c.values[3] & 3u) << 30); + StoreU32(map, pack); + break; + default: + break; + } +} + +static float SrgbToLinear(float value) +{ + return value <= 0.04045f ? value / 12.92f : powf((value + 0.055f) / 1.055f, 2.4f); +} + +static float LinearToSrgb(float value) +{ + return value <= 0.0031308f ? value * 12.92f : 1.055f * powf(value, 1.0f / 2.4f) - 0.055f; +} + +PhiBlitFloat4 PhiConvertBlitFloat4(PhiBlitFloat4 color, + PhiFormat src_format, + PhiFormat dst_format, + int allow_srgb_conversion, + int apply_srgb_conversion) +{ + float scale[4]; + unsigned i; + if(IsFloat(src_format) && !IsFloat(dst_format)) + { + ScaleForFormat(dst_format, scale); + for(i = 0; i < 4; ++i) + { + float low = IsUnsignedComponent(dst_format, i) ? 0.0f : -scale[i]; + color.values[i] = Clamp(color.values[i], low, scale[i]); + } + } + if(allow_srgb_conversion && ((IsSrgb(src_format) && apply_srgb_conversion) || IsSrgb(dst_format))) + { + for(i = 0; i < 3; ++i) + color.values[i] = + (IsSrgb(src_format) && apply_srgb_conversion) ? SrgbToLinear(color.values[i]) : LinearToSrgb(color.values[i]); + } + if(!IsUnsignedComponent(src_format, 0u) && IsUnsignedComponent(dst_format, 0u)) + for(i = 0; i < 4; ++i) + if(color.values[i] < 0.0f) + color.values[i] = 0.0f; + return color; +} diff --git a/src/phi/mic/BlitFormats.h b/src/phi/mic/BlitFormats.h new file mode 100644 index 0000000..4a00d77 --- /dev/null +++ b/src/phi/mic/BlitFormats.h @@ -0,0 +1,43 @@ +#ifndef APE_PHI_BLIT_FORMATS_H +#define APE_PHI_BLIT_FORMATS_H + +#include +#include + +typedef struct PhiBlitFormatInfo +{ + uint32_t texel_size; + uint8_t is_integer; + uint8_t is_signed; + uint8_t is_float; + uint8_t is_srgb; + uint8_t is_unsigned; + uint8_t can_read_float; + uint8_t can_write_float; + uint8_t can_read_int; + uint8_t can_write_int; + uint8_t vector_unorm8x4; +} PhiBlitFormatInfo; + +typedef struct PhiBlitFloat4 +{ + float values[4]; +} PhiBlitFloat4; + +typedef struct PhiBlitInt4 +{ + uint32_t values[4]; +} PhiBlitInt4; + +int PhiGetBlitFormatInfo(PhiFormat format, PhiBlitFormatInfo* info); +PhiBlitFloat4 PhiReadBlitFloat4(const uint8_t* map, PhiFormat format); +void PhiWriteBlitFloat4(PhiBlitFloat4 color, uint8_t* map, PhiFormat format); +PhiBlitInt4 PhiReadBlitInt4(const uint8_t* map, PhiFormat format); +void PhiWriteBlitInt4(PhiBlitInt4 color, uint8_t* map, PhiFormat format); +PhiBlitFloat4 PhiConvertBlitFloat4(PhiBlitFloat4 color, + PhiFormat src_format, + PhiFormat dst_format, + int allow_srgb_conversion, + int apply_srgb_conversion); + +#endif diff --git a/src/phi/mic/Blitter.c b/src/phi/mic/Blitter.c index c9f127b..d573329 100644 --- a/src/phi/mic/Blitter.c +++ b/src/phi/mic/Blitter.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,7 +9,6 @@ #include #include -#define UNORM8_SCALE (1.0f / 255.0f) #define BLIT_WEIGHT_SCALE 1024.0f #define BLIT_PARALLEL_MIN_PIXELS (256u * 1024u) #define BLIT_TASK_TARGET_BYTES (64u * 1024u) @@ -20,18 +20,8 @@ enum PHI_FILTER_LINEAR = 1, }; -typedef struct Color -{ - float r; - float g; - float b; - float a; -} Color; - -typedef struct FormatInfo -{ - uint32_t texel_size; -} FormatInfo; +typedef PhiBlitFloat4 Color; +typedef PhiBlitFormatInfo FormatInfo; typedef struct SampleCoordinate { @@ -51,149 +41,11 @@ typedef struct BlitWork uint64_t rows_per_layer; } BlitWork; -static int GetFormatInfo(PhiFormat format, FormatInfo* info) -{ - switch(format) - { - case PHI_FORMAT_R8_UNORM: - info->texel_size = 1; - return 1; - - case PHI_FORMAT_R8G8_UNORM: - info->texel_size = 2; - return 1; - - case PHI_FORMAT_R8G8B8_UNORM: - case PHI_FORMAT_B8G8R8_UNORM: - info->texel_size = 3; - return 1; - - case PHI_FORMAT_R8G8B8A8_UNORM: - case PHI_FORMAT_B8G8R8A8_UNORM: - case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: - info->texel_size = 4; - return 1; - - default: - return 0; - } -} - -static Color ReadUnorm8(const uint8_t* texel, PhiFormat format) -{ - Color color = { 0.0f, 0.0f, 0.0f, 1.0f }; - - switch(format) - { - case PHI_FORMAT_R8_UNORM: - color.r = (float)texel[0] * UNORM8_SCALE; - break; - - case PHI_FORMAT_R8G8_UNORM: - color.r = (float)texel[0] * UNORM8_SCALE; - color.g = (float)texel[1] * UNORM8_SCALE; - break; - - case PHI_FORMAT_R8G8B8_UNORM: - color.r = (float)texel[0] * UNORM8_SCALE; - color.g = (float)texel[1] * UNORM8_SCALE; - color.b = (float)texel[2] * UNORM8_SCALE; - break; - - case PHI_FORMAT_B8G8R8_UNORM: - color.r = (float)texel[2] * UNORM8_SCALE; - color.g = (float)texel[1] * UNORM8_SCALE; - color.b = (float)texel[0] * UNORM8_SCALE; - break; - - case PHI_FORMAT_R8G8B8A8_UNORM: - case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: - color.r = (float)texel[0] * UNORM8_SCALE; - color.g = (float)texel[1] * UNORM8_SCALE; - color.b = (float)texel[2] * UNORM8_SCALE; - color.a = (float)texel[3] * UNORM8_SCALE; - break; - - case PHI_FORMAT_B8G8R8A8_UNORM: - color.r = (float)texel[2] * UNORM8_SCALE; - color.g = (float)texel[1] * UNORM8_SCALE; - color.b = (float)texel[0] * UNORM8_SCALE; - color.a = (float)texel[3] * UNORM8_SCALE; - break; - - default: - break; - } - - return color; -} - -static inline uint8_t QuantizeUnorm8(float value) -{ - if(value <= 0.0f) - return 0; - if(value >= 1.0f) - return UINT8_MAX; - return (uint8_t)(value * 255.0f + 0.5f); -} - -static void WriteUnorm8(uint8_t* texel, PhiFormat format, Color color) -{ - const uint8_t r = QuantizeUnorm8(color.r); - const uint8_t g = QuantizeUnorm8(color.g); - const uint8_t b = QuantizeUnorm8(color.b); - const uint8_t a = QuantizeUnorm8(color.a); - - switch(format) - { - case PHI_FORMAT_R8_UNORM: - texel[0] = r; - break; - - case PHI_FORMAT_R8G8_UNORM: - texel[0] = r; - texel[1] = g; - break; - - case PHI_FORMAT_R8G8B8_UNORM: - texel[0] = r; - texel[1] = g; - texel[2] = b; - break; - - case PHI_FORMAT_B8G8R8_UNORM: - texel[0] = b; - texel[1] = g; - texel[2] = r; - break; - - case PHI_FORMAT_R8G8B8A8_UNORM: - case PHI_FORMAT_A8B8G8R8_UNORM_PACK32: - texel[0] = r; - texel[1] = g; - texel[2] = b; - texel[3] = a; - break; - - case PHI_FORMAT_B8G8R8A8_UNORM: - texel[0] = b; - texel[1] = g; - texel[2] = r; - texel[3] = a; - break; - - default: - break; - } -} - static inline Color LerpColor(Color a, Color b, float factor) { Color result; - result.r = a.r + (b.r - a.r) * factor; - result.g = a.g + (b.g - a.g) * factor; - result.b = a.b + (b.b - a.b) * factor; - result.a = a.a + (b.a - a.a) * factor; + for(uint32_t component = 0; component < 4; ++component) + result.values[component] = a.values[component] + (b.values[component] - a.values[component]) * factor; return result; } @@ -238,59 +90,133 @@ static Color ReadTexel(const uint8_t* src, uint32_t z) { const uint64_t offset = (uint64_t)z * slice_pitch + (uint64_t)y * row_pitch + (uint64_t)x * texel_size; - return ReadUnorm8(src + (size_t)offset, format); + return PhiReadBlitFloat4(src + (size_t)offset, format); +} + +static inline Color PrepareLinearSample(Color color, + const PhiCmdBlitImage* command, + const FormatInfo* src_info, + int* apply_srgb_conversion) +{ + if(command->allow_srgb_conversion && src_info->is_srgb) + { + *apply_srgb_conversion = 0; + return PhiConvertBlitFloat4(color, (PhiFormat)command->src_format, (PhiFormat)command->dst_format, 1, 1); + } + return color; } static Color -Sample(const uint8_t* src, const PhiCmdBlitImage* command, uint32_t texel_size, float x, float y, float z, int filter_3d) +Sample(const uint8_t* src, const PhiCmdBlitImage* command, const FormatInfo* src_info, float x, float y, float z, int filter_3d) { const PhiFormat format = (PhiFormat)command->src_format; + int apply_srgb_conversion = 1; if(command->filter == PHI_FILTER_NEAREST) { - return ReadTexel(src, - format, - texel_size, - command->src_row_pitch, - command->src_slice_pitch, - GetNearestCoordinate(x, command->src_width), - GetNearestCoordinate(y, command->src_height), - GetNearestCoordinate(z, command->src_depth)); + Color color = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + GetNearestCoordinate(x, command->src_width), + GetNearestCoordinate(y, command->src_height), + GetNearestCoordinate(z, command->src_depth)); + return PhiConvertBlitFloat4(color, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, 1); } const SampleCoordinate sample_x = GetLinearCoordinate(x, command->src_width); const SampleCoordinate sample_y = GetLinearCoordinate(y, command->src_height); const SampleCoordinate sample_z = GetLinearCoordinate(z, command->src_depth); - const Color color_0_0 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.lo, sample_z.lo); - const Color color_0_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.lo, sample_z.lo); - const Color color_1_0 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.hi, sample_z.lo); - const Color color_1_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.hi, sample_z.lo); + Color color_0_0 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.lo, + sample_y.lo, + sample_z.lo); + Color color_0_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.hi, + sample_y.lo, + sample_z.lo); + Color color_1_0 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.lo, + sample_y.hi, + sample_z.lo); + Color color_1_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.hi, + sample_y.hi, + sample_z.lo); + color_0_0 = PrepareLinearSample(color_0_0, command, src_info, &apply_srgb_conversion); + color_0_1 = PrepareLinearSample(color_0_1, command, src_info, &apply_srgb_conversion); + color_1_0 = PrepareLinearSample(color_1_0, command, src_info, &apply_srgb_conversion); + color_1_1 = PrepareLinearSample(color_1_1, command, src_info, &apply_srgb_conversion); const Color row_0 = LerpColor(color_0_0, color_0_1, sample_x.factor); const Color row_1 = LerpColor(color_1_0, color_1_1, sample_x.factor); const Color slice_0 = LerpColor(row_0, row_1, sample_y.factor); if(!filter_3d) - return slice_0; + return PhiConvertBlitFloat4( + slice_0, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, apply_srgb_conversion); - const Color color_0_0_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.lo, sample_z.hi); - const Color color_0_1_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.lo, sample_z.hi); - const Color color_1_0_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.lo, sample_y.hi, sample_z.hi); - const Color color_1_1_1 = ReadTexel( - src, format, texel_size, command->src_row_pitch, command->src_slice_pitch, sample_x.hi, sample_y.hi, sample_z.hi); + Color color_0_0_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.lo, + sample_y.lo, + sample_z.hi); + Color color_0_1_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.hi, + sample_y.lo, + sample_z.hi); + Color color_1_0_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.lo, + sample_y.hi, + sample_z.hi); + Color color_1_1_1 = ReadTexel(src, + format, + src_info->texel_size, + command->src_row_pitch, + command->src_slice_pitch, + sample_x.hi, + sample_y.hi, + sample_z.hi); + color_0_0_1 = PrepareLinearSample(color_0_0_1, command, src_info, &apply_srgb_conversion); + color_0_1_1 = PrepareLinearSample(color_0_1_1, command, src_info, &apply_srgb_conversion); + color_1_0_1 = PrepareLinearSample(color_1_0_1, command, src_info, &apply_srgb_conversion); + color_1_1_1 = PrepareLinearSample(color_1_1_1, command, src_info, &apply_srgb_conversion); const Color row_0_1 = LerpColor(color_0_0_1, color_0_1_1, sample_x.factor); const Color row_1_1 = LerpColor(color_1_0_1, color_1_1_1, sample_x.factor); const Color slice_1 = LerpColor(row_0_1, row_1_1, sample_y.factor); - return LerpColor(slice_0, slice_1, sample_z.factor); + Color color = LerpColor(slice_0, slice_1, sample_z.factor); + return PhiConvertBlitFloat4( + color, format, (PhiFormat)command->dst_format, command->allow_srgb_conversion, apply_srgb_conversion); } static inline int IsMemoryRangeValid(const Memory* memory, uint64_t offset, uint64_t size) @@ -425,13 +351,26 @@ static inline void BlitScalarPixel(uint8_t* dst, const uint8_t* src, const PhiCmdBlitImage* command, const FormatInfo* src_info, + const FormatInfo* dst_info, float source_x, float source_y, float source_z, int filter_3d) { - const Color color = Sample(src, command, src_info->texel_size, source_x, source_y, source_z, filter_3d); - WriteUnorm8(dst, (PhiFormat)command->dst_format, color); + if(src_info->is_integer && dst_info->is_integer) + { + const uint32_t x = GetNearestCoordinate(source_x, command->src_width); + const uint32_t y = GetNearestCoordinate(source_y, command->src_height); + const uint32_t z = GetNearestCoordinate(source_z, command->src_depth); + const uint64_t offset = + (uint64_t)z * command->src_slice_pitch + (uint64_t)y * command->src_row_pitch + (uint64_t)x * src_info->texel_size; + const PhiBlitInt4 color = PhiReadBlitInt4(src + (size_t)offset, (PhiFormat)command->src_format); + PhiWriteBlitInt4(color, dst, (PhiFormat)command->dst_format); + return; + } + + const Color color = Sample(src, command, src_info, source_x, source_y, source_z, filter_3d); + PhiWriteBlitFloat4(color, dst, (PhiFormat)command->dst_format); } static void BlitRow(uint8_t* dst, @@ -448,7 +387,8 @@ static void BlitRow(uint8_t* dst, // Fast path if(command->filter == PHI_FILTER_NEAREST && command->src_format == command->dst_format && command->step_x == 1.0f && - first_source_x >= 0.0f && first_source_x + (float)(pixel_count - 1) < (float)command->src_width) + !(command->allow_srgb_conversion && (src_info->is_srgb || dst_info->is_srgb)) && first_source_x >= 0.0f && + first_source_x + (float)(pixel_count - 1) < (float)command->src_width) { const uint32_t source_texel_x = GetNearestCoordinate(first_source_x, command->src_width); const uint32_t source_texel_y = GetNearestCoordinate(source_y, command->src_height); @@ -461,13 +401,14 @@ static void BlitRow(uint8_t* dst, } uint32_t processed = 0; - if(src_info->texel_size == 4 && dst_info->texel_size == 4 && command->src_width <= INT32_MAX) + if(src_info->vector_unorm8x4 && dst_info->vector_unorm8x4 && command->src_width <= INT32_MAX) { while(processed < pixel_count && ((uintptr_t)(dst + (size_t)processed * 4) & 63) != 0) { const int32_t destination_x = command->dst_x0 + (int32_t)processed; const float source_x = command->src_x0 + (float)destination_x * command->step_x; - BlitScalarPixel(dst + (size_t)processed * 4, src, command, src_info, source_x, source_y, source_z, filter_3d); + BlitScalarPixel( + dst + (size_t)processed * 4, src, command, src_info, dst_info, source_x, source_y, source_z, filter_3d); ++processed; } @@ -536,8 +477,15 @@ static void BlitRow(uint8_t* dst, { const int32_t destination_x = command->dst_x0 + (int32_t)processed; const float source_x = command->src_x0 + (float)destination_x * command->step_x; - BlitScalarPixel( - dst + (size_t)processed * dst_info->texel_size, src, command, src_info, source_x, source_y, source_z, filter_3d); + BlitScalarPixel(dst + (size_t)processed * dst_info->texel_size, + src, + command, + src_info, + dst_info, + source_x, + source_y, + source_z, + filter_3d); ++processed; } } @@ -581,12 +529,21 @@ PhiStatus BlitImage(const PhiCmdBlitImage* command) FormatInfo src_info; FormatInfo dst_info; - if(!GetFormatInfo((PhiFormat)command->src_format, &src_info) || !GetFormatInfo((PhiFormat)command->dst_format, &dst_info)) + if(!PhiGetBlitFormatInfo((PhiFormat)command->src_format, &src_info) || + !PhiGetBlitFormatInfo((PhiFormat)command->dst_format, &dst_info)) { LogErrorFmt("Unsupported blit image formats: src=%u dst=%u", command->src_format, command->dst_format); return PHI_STATUS_INVALID_ARGUMENT; } + const int integer_path = src_info.is_integer && dst_info.is_integer; + if((integer_path && (!src_info.can_read_int || !dst_info.can_write_int)) || + (!integer_path && (!src_info.can_read_float || !dst_info.can_write_float))) + { + LogErrorFmt("Unsupported blit image format direction: src=%u dst=%u", command->src_format, command->dst_format); + return PHI_STATUS_INVALID_ARGUMENT; + } + PhiStatus status = ValidateCommand(command, src_memory, dst_memory, &src_info, &dst_info); if(status != PHI_STATUS_OK) return status; diff --git a/src/phi/mic/CommandBuffer.c b/src/phi/mic/CommandBuffer.c index 7c4f894..b9032dd 100644 --- a/src/phi/mic/CommandBuffer.c +++ b/src/phi/mic/CommandBuffer.c @@ -7,7 +7,7 @@ #include static const char* CommandName[] = { - "CopyBuffer", "FillBuffer", "CopyBufferToImage", "CopyImageToBuffer", "CopyImage", + "CopyBuffer", "FillBuffer", "CopyBufferToImage", "CopyImageToBuffer", "CopyImage", "BlitImage", }; PhiStatus ReadCommandData(PhiCommandReader* reader, void* data, uint64_t size) diff --git a/src/phi/mic/Memory.c b/src/phi/mic/Memory.c index f02c1bf..c57b530 100644 --- a/src/phi/mic/Memory.c +++ b/src/phi/mic/Memory.c @@ -82,7 +82,9 @@ int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header) if(memory == NULL) LogErrorFmt("Failed to allocate %zu bytes", (size_t)request.size); else - LogInfoFmt("Allocated %llu bytes to handle 0x%X", request.size, (uintptr_t)memory); + LogInfoFmt("Allocated %llu bytes to handle 0x%llX", + (unsigned long long)request.size, + (unsigned long long)(uintptr_t)memory); } else if(header->type == PHI_PACKET_MAP_HOST_MEMORY) { @@ -93,7 +95,7 @@ int HandleNewMemory(PhiEndpoint endpoint, const PhiMessageHeader* header) if(memory == NULL) reply.result.status = PHI_STATUS_MAP_HOST_MEMORY_FAILED; else - LogInfoFmt("Mapped host memory to handle 0x%X", (uint64_t)(uintptr_t)memory); + LogInfoFmt("Mapped host memory to handle 0x%llX", (unsigned long long)(uintptr_t)memory); } if(memory != NULL) @@ -131,20 +133,36 @@ int HandleDestroyMemory(PhiEndpoint endpoint, const PhiMessageHeader* header) if(request.remote_handle == 0) { reply.result.status = PHI_STATUS_INVALID_HANDLE; - LogErrorFmt("Could not free memory: invalid handle 0x%X", request.remote_handle); + LogErrorFmt("Could not free memory: invalid handle 0x%llX", (unsigned long long)request.remote_handle); } else { - const Memory* memory = (const Memory*)(uintptr_t)request.remote_handle; + Memory* memory = (Memory*)(uintptr_t)request.remote_handle; + const MemoryType memory_type = memory->type; + const char* memory_type_name; - if(memory->type == PHI_MEMORY_LOCAL) - free((void*)memory); - else if(memory->type == PHI_MEMORY_HOST_MAPPED) - scif_munmap((void*)memory->ptr, memory->size); + if(memory_type == PHI_MEMORY_LOCAL) + memory_type_name = "local"; + else if(memory_type == PHI_MEMORY_HOST_MAPPED) + memory_type_name = "host-mapped"; + else + { + reply.result.status = PHI_STATUS_INVALID_HANDLE; + LogErrorFmt("Could not free memory handle 0x%llX: invalid memory type", (unsigned long long)request.remote_handle); + return SendReply(endpoint, header, &reply, sizeof(reply)); + } - LogInfoFmt("Destroyed %s memory handle 0x%X", - memory->type == PHI_MEMORY_LOCAL ? "local" : "host-mapped", - request.remote_handle); + if(memory_type == PHI_MEMORY_HOST_MAPPED && scif_munmap(memory->ptr, (size_t)memory->scif_size) != 0) + { + reply.result.status = PHI_STATUS_INVALID_HANDLE; + LogErrorFmt("Failed to unmap memory handle 0x%llX: %s", (unsigned long long)request.remote_handle, strerror(errno)); + } + else + { + LogInfoFmt("Destroyed %s memory handle 0x%llX", memory_type_name, (unsigned long long)request.remote_handle); + } + + free(memory); } return SendReply(endpoint, header, &reply, sizeof(reply)); diff --git a/src/phi/mic/Queue.c b/src/phi/mic/Queue.c index f6df104..fcd26e9 100644 --- a/src/phi/mic/Queue.c +++ b/src/phi/mic/Queue.c @@ -49,16 +49,21 @@ static int SendQueueCompletion(PhiEndpoint endpoint, uint64_t sequence, PhiStatu static int RunQueue(PhiEndpoint endpoint, volatile PhiQueueShared* shared) { uint64_t next_sequence = 1; - PhiStatus fatal_status = PHI_STATUS_OK; for(;;) { PhiQueueDoorbell doorbell; if(ReadAll(endpoint, &doorbell, sizeof(doorbell)) < 0) + { + LogWarningFmt("Queue peer disconnected while waiting for sequence %llu", (unsigned long long)next_sequence); return 0; + } if(doorbell.sequence == PHI_QUEUE_SHUTDOWN_SEQUENCE) + { + LogInfoFmt("Received queue shutdown doorbell at sequence %llu", (unsigned long long)next_sequence); return 1; + } if(doorbell.sequence < next_sequence) continue; @@ -76,20 +81,14 @@ static int RunQueue(PhiEndpoint endpoint, volatile PhiQueueShared* shared) .command_count = remote_submission->command_count, }; - PhiStatus status = fatal_status; - if(status == PHI_STATUS_OK) - { - if(submission.sequence != next_sequence) - status = PHI_STATUS_BAD_MESSAGE; - else - status = ExecuteQueueSubmission(endpoint, &submission); - } + PhiStatus status; + if(submission.sequence != next_sequence) + status = PHI_STATUS_BAD_MESSAGE; + else + status = ExecuteQueueSubmission(endpoint, &submission); - if(status != PHI_STATUS_OK && fatal_status == PHI_STATUS_OK) - { + if(status != PHI_STATUS_OK) LogErrorFmt("Queue submission %llu failed: %s", (unsigned long long)next_sequence, StatusName[status]); - fatal_status = status; - } __atomic_store_n(&shared->completed_sequence, next_sequence, __ATOMIC_RELEASE); if(SendQueueCompletion(endpoint, next_sequence, status) < 0) @@ -143,10 +142,22 @@ int HandleQueueSetup(PhiEndpoint endpoint, const PhiMessageHeader* header) const int run_result = RunQueue(endpoint, shared); if(scif_munmap((void*)shared, (size_t)request.scif_size) != 0) + { + LogErrorFmt("Failed to unmap queue ring during shutdown: %s", strerror(errno)); return -1; + } if(run_result == 1) - return SendQueueCompletion(endpoint, PHI_QUEUE_SHUTDOWN_SEQUENCE, PHI_STATUS_OK); + { + LogInfo("Queue ring unmapped; sending shutdown acknowledgement"); + if(SendQueueCompletion(endpoint, PHI_QUEUE_SHUTDOWN_SEQUENCE, PHI_STATUS_OK) < 0) + { + LogErrorFmt("Failed to send queue shutdown acknowledgement: %s", strerror(errno)); + return -1; + } + LogInfo("Queue shutdown acknowledgement sent"); + return 0; + } return run_result; } diff --git a/src/phi/shared/Protocol.h b/src/phi/shared/Protocol.h index 86978fd..5468054 100644 --- a/src/phi/shared/Protocol.h +++ b/src/phi/shared/Protocol.h @@ -157,6 +157,7 @@ typedef enum PhiFormat PHI_FORMAT_R32G32B32A32_SINT = 108, PHI_FORMAT_R32G32B32A32_SFLOAT = 109, PHI_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + PHI_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, PHI_FORMAT_D16_UNORM = 124, PHI_FORMAT_X8_D24_UNORM_PACK32 = 125, PHI_FORMAT_D32_SFLOAT = 126, @@ -164,6 +165,9 @@ typedef enum PhiFormat PHI_FORMAT_D16_UNORM_S8_UINT = 128, PHI_FORMAT_D24_UNORM_S8_UINT = 129, PHI_FORMAT_D32_SFLOAT_S8_UINT = 130, + + PHI_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000, + PHI_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001, } PhiFormat; typedef struct PhiMessageHeader