feat(host): vendor PyroWave + minimal Granite subset as crates/pyrowave-sys
Phase 0 of design/pyrowave-codec-plan.md — the opt-in wired-LAN ultra-low- latency codec. Vendored at upstream 509e4f88 (API 0.4.0, Granite 44362775, volk + vulkan-headers pins in PUNKTFUNK-VENDOR.txt), pruned to the 6.6 MB the standalone no-renderer build needs; scripts/vendor-pyrowave.sh reproduces the tree (a pin bump is protocol-affecting, plan §4.2). build.rs drives the wrapper CMakeLists (static archives incl. a static C-API lib upstream only ships shared) + bindgen over pyrowave.h; Linux and Windows only, empty stub elsewhere (Apple gets a native Metal port, §4.7). Offline-safe by construction: no network, no system lib, vendored Vulkan headers — same model as the opus dep (flatpak builder has no network). Phase-0 validation on .21 (RTX 5070 Ti, driver 610.43.03): - upstream pyrowave-c-test + interop test (incl. dmabuf/DRM-modifier Vulkan<->Vulkan) pass, from the pristine AND the pruned tree - GPU kernel times at ~1.6 bpp noise: encode/decode 0.090/0.042 ms @800p, 0.146/0.067 @1080p, 0.226/0.103 @1440p, 0.477/0.201 @4K — order of magnitude under NVENC's 1-2 ms retrieve, CBR lands within ~100 B of target - cargo test -p pyrowave-sys green (static link + API-version pin check) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+346
@@ -0,0 +1,346 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "memory_mapped_texture.hpp"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
struct MemoryMappedHeader
|
||||
{
|
||||
char magic[16];
|
||||
VkImageType type;
|
||||
VkFormat format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t depth;
|
||||
uint32_t layers;
|
||||
uint32_t levels;
|
||||
uint32_t flags;
|
||||
uint64_t payload_size;
|
||||
uint64_t reserved1;
|
||||
};
|
||||
static const size_t header_size = 16 + 8 * 4 + 2 * 8;
|
||||
static_assert(sizeof(MemoryMappedHeader) == header_size, "Header size is not properly packed.");
|
||||
|
||||
static const char MAGIC[16] = "GRANITE TEXFMT1";
|
||||
|
||||
void MemoryMappedTexture::set_generate_mipmaps_on_load(bool enable)
|
||||
{
|
||||
mipgen_on_load = enable;
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_flags(MemoryMappedTextureFlags flags)
|
||||
{
|
||||
bool new_cube = (flags & MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT) != 0;
|
||||
if (new_cube != cube)
|
||||
abort();
|
||||
set_generate_mipmaps_on_load((flags & MEMORY_MAPPED_TEXTURE_GENERATE_MIPMAP_ON_LOAD_BIT) != 0);
|
||||
}
|
||||
|
||||
MemoryMappedTextureFlags MemoryMappedTexture::get_flags() const
|
||||
{
|
||||
MemoryMappedTextureFlags flags = 0;
|
||||
if (cube)
|
||||
flags |= MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT;
|
||||
if (mipgen_on_load)
|
||||
flags |= MEMORY_MAPPED_TEXTURE_GENERATE_MIPMAP_ON_LOAD_BIT;
|
||||
|
||||
flags |= swizzle.r << MEMORY_MAPPED_TEXTURE_SWIZZLE_R_SHIFT;
|
||||
flags |= swizzle.g << MEMORY_MAPPED_TEXTURE_SWIZZLE_G_SHIFT;
|
||||
flags |= swizzle.b << MEMORY_MAPPED_TEXTURE_SWIZZLE_B_SHIFT;
|
||||
flags |= swizzle.a << MEMORY_MAPPED_TEXTURE_SWIZZLE_A_SHIFT;
|
||||
return flags;
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_1d(VkFormat format, uint32_t width, uint32_t layers, uint32_t levels)
|
||||
{
|
||||
layout.set_1d(format, width, layers, levels);
|
||||
cube = false;
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_2d(VkFormat format, uint32_t width, uint32_t height, uint32_t layers, uint32_t levels)
|
||||
{
|
||||
layout.set_2d(format, width, height, layers, levels);
|
||||
cube = false;
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_3d(VkFormat format, uint32_t width, uint32_t height, uint32_t depth, uint32_t levels)
|
||||
{
|
||||
layout.set_3d(format, width, height, depth, levels);
|
||||
cube = false;
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_cube(VkFormat format, uint32_t size, uint32_t cube_layers, uint32_t levels)
|
||||
{
|
||||
layout.set_2d(format, size, size, cube_layers * 6, levels);
|
||||
cube = true;
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::copy_to_path(Granite::Filesystem &fs, const std::string &path)
|
||||
{
|
||||
if (layout.get_required_size() == 0 || !mapped)
|
||||
return false;
|
||||
|
||||
auto target_file = fs.open(path, Granite::FileMode::WriteOnly);
|
||||
if (!target_file)
|
||||
return false;
|
||||
|
||||
auto new_mapped = target_file->map_write(get_required_size());
|
||||
if (!new_mapped)
|
||||
return false;
|
||||
|
||||
memcpy(new_mapped->mutable_data(), mapped, get_required_size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_write(Granite::FileMappingHandle new_file)
|
||||
{
|
||||
file = std::move(new_file);
|
||||
mapped = file->mutable_data<uint8_t>();
|
||||
|
||||
MemoryMappedHeader header = {};
|
||||
memcpy(header.magic, MAGIC, sizeof(MAGIC));
|
||||
header.width = layout.get_width();
|
||||
header.height = layout.get_height();
|
||||
header.depth = layout.get_depth();
|
||||
header.flags = get_flags();
|
||||
header.layers = layout.get_layers();
|
||||
header.levels = layout.get_levels();
|
||||
header.payload_size = layout.get_required_size();
|
||||
header.type = layout.get_image_type();
|
||||
header.format = layout.get_format();
|
||||
memcpy(mapped, &header, sizeof(header));
|
||||
|
||||
layout.set_buffer(mapped + sizeof(header), layout.get_required_size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_write(Granite::Filesystem &fs, const std::string &path)
|
||||
{
|
||||
if (layout.get_required_size() == 0)
|
||||
return false;
|
||||
|
||||
auto new_file = fs.open(path, Granite::FileMode::WriteOnly);
|
||||
if (!new_file)
|
||||
return false;
|
||||
|
||||
auto map_handle = new_file->map_write(get_required_size());
|
||||
if (!map_handle)
|
||||
return false;
|
||||
|
||||
return map_write(std::move(map_handle));
|
||||
}
|
||||
|
||||
struct ScratchFile final : Granite::File
|
||||
{
|
||||
ScratchFile(const void *mapped, size_t size)
|
||||
{
|
||||
data.resize(size);
|
||||
if (mapped)
|
||||
memcpy(data.data(), mapped, size);
|
||||
}
|
||||
|
||||
Granite::FileMappingHandle map_subset(uint64_t offset, size_t range) override
|
||||
{
|
||||
if (offset + range > data.size())
|
||||
return {};
|
||||
|
||||
return Util::make_handle<Granite::FileMapping>(
|
||||
reference_from_this(), offset,
|
||||
data.data() + offset, range,
|
||||
0, range);
|
||||
}
|
||||
|
||||
Granite::FileMappingHandle map_write(size_t size) override
|
||||
{
|
||||
data.resize(size);
|
||||
return map_subset(0, size);
|
||||
}
|
||||
|
||||
void unmap(void *, size_t) override
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t get_size() override
|
||||
{
|
||||
return data.size();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
void MemoryMappedTexture::make_local_copy()
|
||||
{
|
||||
if (empty())
|
||||
return;
|
||||
|
||||
auto new_file = Util::make_handle<ScratchFile>(mapped, get_required_size());
|
||||
file = new_file->map();
|
||||
mapped = file->mutable_data<uint8_t>();
|
||||
layout.set_buffer(mapped + sizeof(MemoryMappedHeader),
|
||||
get_required_size() - sizeof(MemoryMappedHeader));
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_write_scratch()
|
||||
{
|
||||
if (layout.get_required_size() == 0)
|
||||
return false;
|
||||
|
||||
auto new_file = Util::make_handle<ScratchFile>(nullptr, get_required_size());
|
||||
if (new_file->get_size() < sizeof(MemoryMappedHeader))
|
||||
return false;
|
||||
|
||||
auto new_mapped = new_file->map_write(get_required_size());
|
||||
return map_write(std::move(new_mapped));
|
||||
}
|
||||
|
||||
size_t MemoryMappedTexture::get_required_size() const
|
||||
{
|
||||
return layout.get_required_size() + sizeof(MemoryMappedHeader);
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::set_swizzle(const VkComponentMapping &swizzle_)
|
||||
{
|
||||
swizzle = swizzle_;
|
||||
}
|
||||
|
||||
static void remap(VkComponentSwizzle &output, VkComponentSwizzle input,
|
||||
const VkComponentMapping &mapping, VkComponentSwizzle identity)
|
||||
{
|
||||
if (input == VK_COMPONENT_SWIZZLE_IDENTITY)
|
||||
input = identity;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case VK_COMPONENT_SWIZZLE_R:
|
||||
output = mapping.r;
|
||||
break;
|
||||
|
||||
case VK_COMPONENT_SWIZZLE_G:
|
||||
output = mapping.g;
|
||||
break;
|
||||
|
||||
case VK_COMPONENT_SWIZZLE_B:
|
||||
output = mapping.b;
|
||||
break;
|
||||
|
||||
case VK_COMPONENT_SWIZZLE_A:
|
||||
output = mapping.a;
|
||||
break;
|
||||
|
||||
case VK_COMPONENT_SWIZZLE_ONE:
|
||||
case VK_COMPONENT_SWIZZLE_ZERO:
|
||||
output = input;
|
||||
break;
|
||||
|
||||
default:
|
||||
output = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryMappedTexture::remap_swizzle(VkComponentMapping &mapping) const
|
||||
{
|
||||
VkComponentMapping new_mapping;
|
||||
|
||||
remap(new_mapping.r, swizzle.r, mapping, VK_COMPONENT_SWIZZLE_R);
|
||||
remap(new_mapping.g, swizzle.g, mapping, VK_COMPONENT_SWIZZLE_G);
|
||||
remap(new_mapping.b, swizzle.b, mapping, VK_COMPONENT_SWIZZLE_B);
|
||||
remap(new_mapping.a, swizzle.a, mapping, VK_COMPONENT_SWIZZLE_A);
|
||||
mapping = new_mapping;
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_copy(const void *mapped_, size_t size)
|
||||
{
|
||||
auto new_file = Util::make_handle<ScratchFile>(mapped_, size);
|
||||
if (new_file->get_size() < sizeof(MemoryMappedHeader))
|
||||
return false;
|
||||
|
||||
auto new_mapped = new_file->map();
|
||||
return map_read(std::move(new_mapped));
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_read(Granite::FileMappingHandle new_file)
|
||||
{
|
||||
file = std::move(new_file);
|
||||
mapped = const_cast<uint8_t *>(file->data<uint8_t>());
|
||||
|
||||
auto *header = reinterpret_cast<const MemoryMappedHeader *>(mapped);
|
||||
switch (header->type)
|
||||
{
|
||||
case VK_IMAGE_TYPE_1D:
|
||||
layout.set_1d(header->format, header->width, header->layers, header->levels);
|
||||
break;
|
||||
|
||||
case VK_IMAGE_TYPE_2D:
|
||||
layout.set_2d(header->format, header->width, header->height, header->layers, header->levels);
|
||||
break;
|
||||
|
||||
case VK_IMAGE_TYPE_3D:
|
||||
layout.set_3d(header->format, header->width, header->height, header->depth, header->levels);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
cube = (header->flags & MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT) != 0;
|
||||
mipgen_on_load = (header->flags & MEMORY_MAPPED_TEXTURE_GENERATE_MIPMAP_ON_LOAD_BIT) != 0;
|
||||
swizzle.r = static_cast<VkComponentSwizzle>((header->flags >> MEMORY_MAPPED_TEXTURE_SWIZZLE_R_SHIFT) & MEMORY_MAPPED_TEXTURE_SWIZZLE_MASK);
|
||||
swizzle.g = static_cast<VkComponentSwizzle>((header->flags >> MEMORY_MAPPED_TEXTURE_SWIZZLE_G_SHIFT) & MEMORY_MAPPED_TEXTURE_SWIZZLE_MASK);
|
||||
swizzle.b = static_cast<VkComponentSwizzle>((header->flags >> MEMORY_MAPPED_TEXTURE_SWIZZLE_B_SHIFT) & MEMORY_MAPPED_TEXTURE_SWIZZLE_MASK);
|
||||
swizzle.a = static_cast<VkComponentSwizzle>((header->flags >> MEMORY_MAPPED_TEXTURE_SWIZZLE_A_SHIFT) & MEMORY_MAPPED_TEXTURE_SWIZZLE_MASK);
|
||||
|
||||
if ((layout.get_required_size() + sizeof(MemoryMappedHeader)) < file->get_size())
|
||||
return false;
|
||||
if (header->payload_size != layout.get_required_size())
|
||||
return false;
|
||||
|
||||
layout.set_buffer(static_cast<uint8_t *>(mapped) + sizeof(MemoryMappedHeader), header->payload_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::map_read(Granite::Filesystem &fs, const std::string &path)
|
||||
{
|
||||
auto loaded_file = fs.open(path, Granite::FileMode::ReadOnly);
|
||||
if (!loaded_file)
|
||||
return false;
|
||||
|
||||
if (loaded_file->get_size() < sizeof(MemoryMappedHeader))
|
||||
return false;
|
||||
|
||||
auto new_mapped = loaded_file->map();
|
||||
if (!new_mapped)
|
||||
return false;
|
||||
|
||||
return map_read(std::move(new_mapped));
|
||||
}
|
||||
|
||||
bool MemoryMappedTexture::is_header(const void *mapped_, size_t size)
|
||||
{
|
||||
if (size < sizeof(MemoryMappedHeader))
|
||||
return false;
|
||||
return memcmp(mapped_, MAGIC, sizeof(MAGIC)) == 0;
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "texture_format.hpp"
|
||||
#include "filesystem.hpp"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum MemoryMappedTextureFlagBits
|
||||
{
|
||||
MEMORY_MAPPED_TEXTURE_CUBE_MAP_COMPATIBLE_BIT = 1 << 0,
|
||||
MEMORY_MAPPED_TEXTURE_GENERATE_MIPMAP_ON_LOAD_BIT = 1 << 1,
|
||||
MEMORY_MAPPED_TEXTURE_SWIZZLE_R_SHIFT = 16,
|
||||
MEMORY_MAPPED_TEXTURE_SWIZZLE_G_SHIFT = 19,
|
||||
MEMORY_MAPPED_TEXTURE_SWIZZLE_B_SHIFT = 22,
|
||||
MEMORY_MAPPED_TEXTURE_SWIZZLE_A_SHIFT = 25,
|
||||
MEMORY_MAPPED_TEXTURE_SWIZZLE_MASK = 0x7
|
||||
};
|
||||
using MemoryMappedTextureFlags = uint32_t;
|
||||
|
||||
class MemoryMappedTexture
|
||||
{
|
||||
public:
|
||||
void set_1d(VkFormat format, uint32_t width, uint32_t layers = 1, uint32_t levels = 1);
|
||||
void set_2d(VkFormat format, uint32_t width, uint32_t height, uint32_t layers = 1, uint32_t levels = 1);
|
||||
void set_3d(VkFormat format, uint32_t width, uint32_t height, uint32_t depth, uint32_t levels = 1);
|
||||
void set_cube(VkFormat format, uint32_t size, uint32_t cube_layers = 1, uint32_t levels = 1);
|
||||
|
||||
static bool is_header(const void *mapped, size_t size);
|
||||
|
||||
bool map_write(Granite::Filesystem &fs, const std::string &path);
|
||||
bool map_write(Granite::FileMappingHandle file);
|
||||
bool map_read(Granite::Filesystem &fs, const std::string &path);
|
||||
bool map_read(Granite::FileMappingHandle file);
|
||||
bool map_copy(const void *mapped, size_t size);
|
||||
bool map_write_scratch();
|
||||
bool copy_to_path(Granite::Filesystem &fs, const std::string &path);
|
||||
void make_local_copy();
|
||||
|
||||
inline const Vulkan::TextureFormatLayout &get_layout() const
|
||||
{
|
||||
return layout;
|
||||
}
|
||||
|
||||
void set_generate_mipmaps_on_load(bool enable = true);
|
||||
|
||||
MemoryMappedTextureFlags get_flags() const;
|
||||
void set_flags(MemoryMappedTextureFlags flags);
|
||||
|
||||
size_t get_required_size() const;
|
||||
void set_swizzle(const VkComponentMapping &swizzle);
|
||||
|
||||
void remap_swizzle(VkComponentMapping &mapping) const;
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return get_layout().get_required_size() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Vulkan::TextureFormatLayout layout;
|
||||
Granite::FileMappingHandle file;
|
||||
uint8_t *mapped = nullptr;
|
||||
bool cube = false;
|
||||
bool mipgen_on_load = false;
|
||||
VkComponentMapping swizzle = {
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A,
|
||||
};
|
||||
};
|
||||
}
|
||||
+1425
File diff suppressed because it is too large
Load Diff
+39
@@ -0,0 +1,39 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "texture_format.hpp"
|
||||
#include "command_buffer.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
Vulkan::ImageHandle decode_compressed_image(Vulkan::CommandBuffer &cmd, const Vulkan::TextureFormatLayout &layout,
|
||||
VkFormat preferred_decode_format,
|
||||
const VkComponentMapping &swizzle = {
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A,
|
||||
});
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "texture_files.hpp"
|
||||
#include "stb_image.h"
|
||||
#include "filesystem.hpp"
|
||||
#include "muglm/muglm_impl.hpp"
|
||||
#include <string.h>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
static MemoryMappedTexture load_stb(const void *data, size_t size, ColorSpace color)
|
||||
{
|
||||
int width, height;
|
||||
int components;
|
||||
auto *buffer = stbi_load_from_memory(static_cast<const stbi_uc *>(data), size, &width, &height, &components, 4);
|
||||
|
||||
if (!buffer)
|
||||
return {};
|
||||
|
||||
MemoryMappedTexture tex;
|
||||
tex.set_2d(color == ColorSpace::sRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM, width, height);
|
||||
tex.set_generate_mipmaps_on_load(true);
|
||||
if (!tex.map_write_scratch())
|
||||
return {};
|
||||
|
||||
memcpy(tex.get_layout().data(), buffer, width * height * 4);
|
||||
stbi_image_free(buffer);
|
||||
return tex;
|
||||
}
|
||||
|
||||
static MemoryMappedTexture load_hdr(const void *data, size_t size)
|
||||
{
|
||||
int width, height;
|
||||
int components;
|
||||
auto *buffer = stbi_loadf_from_memory(static_cast<const stbi_uc *>(data), size, &width, &height, &components, 3);
|
||||
|
||||
MemoryMappedTexture tex;
|
||||
tex.set_2d(VK_FORMAT_R16G16B16A16_SFLOAT, width, height);
|
||||
if (!tex.map_write_scratch())
|
||||
return {};
|
||||
tex.set_generate_mipmaps_on_load(true);
|
||||
|
||||
auto *converted = static_cast<muglm::u16vec4 *>(tex.get_layout().data());
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
converted[i] = muglm::floatToHalf(muglm::vec4(buffer[3 * i + 0], buffer[3 * i + 1], buffer[3 * i + 2], 1.0f));
|
||||
}
|
||||
stbi_image_free(buffer);
|
||||
return tex;
|
||||
}
|
||||
|
||||
MemoryMappedTexture load_texture_from_memory(const void *data, size_t size, ColorSpace color)
|
||||
{
|
||||
static const uint8_t png_magic[] = {
|
||||
0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a,
|
||||
};
|
||||
|
||||
static const uint8_t jpg_magic[] = {
|
||||
0xff, 0xd8,
|
||||
};
|
||||
|
||||
static const uint8_t hdr_magic[] = {
|
||||
0x23, 0x3f, 0x52, 0x41, 0x44, 0x49, 0x41, 0x4e, 0x43, 0x45, 0x0a,
|
||||
};
|
||||
|
||||
if (size >= sizeof(png_magic) && memcmp(data, png_magic, sizeof(png_magic)) == 0)
|
||||
return load_stb(data, size, color);
|
||||
else if (size >= 2 && memcmp(data, jpg_magic, sizeof(jpg_magic)) == 0)
|
||||
return load_stb(data, size, color);
|
||||
else if (size >= sizeof(hdr_magic) && memcmp(data, hdr_magic, sizeof(hdr_magic)) == 0)
|
||||
return load_hdr(data, size);
|
||||
else if (MemoryMappedTexture::is_header(data, size))
|
||||
{
|
||||
MemoryMappedTexture mapped;
|
||||
mapped.map_copy(data, size);
|
||||
return mapped;
|
||||
}
|
||||
else
|
||||
{
|
||||
// YOLO!
|
||||
return load_stb(data, size, color);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMappedTexture load_texture_from_file(Granite::Filesystem &fs, const std::string &path, ColorSpace color)
|
||||
{
|
||||
auto file = fs.open(path, Granite::FileMode::ReadOnly);
|
||||
if (!file)
|
||||
return {};
|
||||
|
||||
auto mapped = file->map();
|
||||
if (!mapped)
|
||||
return {};
|
||||
|
||||
if (MemoryMappedTexture::is_header(mapped->data(), mapped->get_size()))
|
||||
{
|
||||
MemoryMappedTexture tex;
|
||||
tex.map_read(std::move(mapped));
|
||||
return tex;
|
||||
}
|
||||
|
||||
return load_texture_from_memory(mapped->data(), mapped->get_size(), color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "format.hpp"
|
||||
#include "memory_mapped_texture.hpp"
|
||||
|
||||
namespace Granite
|
||||
{
|
||||
class Filesystem;
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum class ColorSpace
|
||||
{
|
||||
Linear,
|
||||
sRGB
|
||||
};
|
||||
|
||||
MemoryMappedTexture load_texture_from_file(Granite::Filesystem &fs, const std::string &path, ColorSpace color = ColorSpace::sRGB);
|
||||
MemoryMappedTexture load_texture_from_memory(const void *data, size_t size,
|
||||
ColorSpace color = ColorSpace::sRGB);
|
||||
}
|
||||
+518
@@ -0,0 +1,518 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#define NOMINMAX
|
||||
#include "texture_format.hpp"
|
||||
#include "format.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
uint32_t TextureFormatLayout::num_miplevels(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
uint32_t size = unsigned(std::max(std::max(width, height), depth));
|
||||
uint32_t levels = 0;
|
||||
while (size)
|
||||
{
|
||||
levels++;
|
||||
size >>= 1;
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::format_block_dim(VkFormat format, uint32_t &width, uint32_t &height)
|
||||
{
|
||||
#define fmt(x, w, h) \
|
||||
case VK_FORMAT_##x: \
|
||||
width = w; \
|
||||
height = h; \
|
||||
break
|
||||
|
||||
switch (format)
|
||||
{
|
||||
fmt(ETC2_R8G8B8A8_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A8_SRGB_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A1_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8A1_SRGB_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8_UNORM_BLOCK, 4, 4);
|
||||
fmt(ETC2_R8G8B8_SRGB_BLOCK, 4, 4);
|
||||
fmt(EAC_R11_UNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11_SNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11G11_UNORM_BLOCK, 4, 4);
|
||||
fmt(EAC_R11G11_SNORM_BLOCK, 4, 4);
|
||||
|
||||
fmt(BC1_RGB_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC1_RGB_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC1_RGBA_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC1_RGBA_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC2_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC2_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC3_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC3_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC4_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC4_SNORM_BLOCK, 4, 4);
|
||||
fmt(BC5_UNORM_BLOCK, 4, 4);
|
||||
fmt(BC5_SNORM_BLOCK, 4, 4);
|
||||
fmt(BC6H_UFLOAT_BLOCK, 4, 4);
|
||||
fmt(BC6H_SFLOAT_BLOCK, 4, 4);
|
||||
fmt(BC7_SRGB_BLOCK, 4, 4);
|
||||
fmt(BC7_UNORM_BLOCK, 4, 4);
|
||||
|
||||
#define astc_fmt(w, h) \
|
||||
fmt(ASTC_##w##x##h##_UNORM_BLOCK, w, h); \
|
||||
fmt(ASTC_##w##x##h##_SRGB_BLOCK, w, h); \
|
||||
fmt(ASTC_##w##x##h##_SFLOAT_BLOCK_EXT, w, h)
|
||||
|
||||
astc_fmt(4, 4);
|
||||
astc_fmt(5, 4);
|
||||
astc_fmt(5, 5);
|
||||
astc_fmt(6, 5);
|
||||
astc_fmt(6, 6);
|
||||
astc_fmt(8, 5);
|
||||
astc_fmt(8, 6);
|
||||
astc_fmt(8, 8);
|
||||
astc_fmt(10, 5);
|
||||
astc_fmt(10, 6);
|
||||
astc_fmt(10, 8);
|
||||
astc_fmt(10, 10);
|
||||
astc_fmt(12, 10);
|
||||
astc_fmt(12, 12);
|
||||
|
||||
default:
|
||||
width = 1;
|
||||
height = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
#undef fmt
|
||||
#undef astc_fmt
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::format_block_size(VkFormat format, VkImageAspectFlags aspect)
|
||||
{
|
||||
#define fmt(x, bpp) \
|
||||
case VK_FORMAT_##x: \
|
||||
return bpp
|
||||
|
||||
#define fmt2(x, bpp0, bpp1) \
|
||||
case VK_FORMAT_##x: \
|
||||
return aspect == VK_IMAGE_ASPECT_PLANE_0_BIT ? bpp0 : bpp1
|
||||
|
||||
switch (format)
|
||||
{
|
||||
fmt(R4G4_UNORM_PACK8, 1);
|
||||
fmt(R4G4B4A4_UNORM_PACK16, 2);
|
||||
fmt(B4G4R4A4_UNORM_PACK16, 2);
|
||||
fmt(R5G6B5_UNORM_PACK16, 2);
|
||||
fmt(B5G6R5_UNORM_PACK16, 2);
|
||||
fmt(R5G5B5A1_UNORM_PACK16, 2);
|
||||
fmt(B5G5R5A1_UNORM_PACK16, 2);
|
||||
fmt(A1R5G5B5_UNORM_PACK16, 2);
|
||||
fmt(R8_UNORM, 1);
|
||||
fmt(R8_SNORM, 1);
|
||||
fmt(R8_USCALED, 1);
|
||||
fmt(R8_SSCALED, 1);
|
||||
fmt(R8_UINT, 1);
|
||||
fmt(R8_SINT, 1);
|
||||
fmt(R8_SRGB, 1);
|
||||
fmt(R8G8_UNORM, 2);
|
||||
fmt(R8G8_SNORM, 2);
|
||||
fmt(R8G8_USCALED, 2);
|
||||
fmt(R8G8_SSCALED, 2);
|
||||
fmt(R8G8_UINT, 2);
|
||||
fmt(R8G8_SINT, 2);
|
||||
fmt(R8G8_SRGB, 2);
|
||||
fmt(R8G8B8_UNORM, 3);
|
||||
fmt(R8G8B8_SNORM, 3);
|
||||
fmt(R8G8B8_USCALED, 3);
|
||||
fmt(R8G8B8_SSCALED, 3);
|
||||
fmt(R8G8B8_UINT, 3);
|
||||
fmt(R8G8B8_SINT, 3);
|
||||
fmt(R8G8B8_SRGB, 3);
|
||||
fmt(R8G8B8A8_UNORM, 4);
|
||||
fmt(R8G8B8A8_SNORM, 4);
|
||||
fmt(R8G8B8A8_USCALED, 4);
|
||||
fmt(R8G8B8A8_SSCALED, 4);
|
||||
fmt(R8G8B8A8_UINT, 4);
|
||||
fmt(R8G8B8A8_SINT, 4);
|
||||
fmt(R8G8B8A8_SRGB, 4);
|
||||
fmt(B8G8R8A8_UNORM, 4);
|
||||
fmt(B8G8R8A8_SNORM, 4);
|
||||
fmt(B8G8R8A8_USCALED, 4);
|
||||
fmt(B8G8R8A8_SSCALED, 4);
|
||||
fmt(B8G8R8A8_UINT, 4);
|
||||
fmt(B8G8R8A8_SINT, 4);
|
||||
fmt(B8G8R8A8_SRGB, 4);
|
||||
fmt(A8B8G8R8_UNORM_PACK32, 4);
|
||||
fmt(A8B8G8R8_SNORM_PACK32, 4);
|
||||
fmt(A8B8G8R8_USCALED_PACK32, 4);
|
||||
fmt(A8B8G8R8_SSCALED_PACK32, 4);
|
||||
fmt(A8B8G8R8_UINT_PACK32, 4);
|
||||
fmt(A8B8G8R8_SINT_PACK32, 4);
|
||||
fmt(A8B8G8R8_SRGB_PACK32, 4);
|
||||
fmt(A2B10G10R10_UNORM_PACK32, 4);
|
||||
fmt(A2B10G10R10_SNORM_PACK32, 4);
|
||||
fmt(A2B10G10R10_USCALED_PACK32, 4);
|
||||
fmt(A2B10G10R10_SSCALED_PACK32, 4);
|
||||
fmt(A2B10G10R10_UINT_PACK32, 4);
|
||||
fmt(A2B10G10R10_SINT_PACK32, 4);
|
||||
fmt(A2R10G10B10_UNORM_PACK32, 4);
|
||||
fmt(A2R10G10B10_SNORM_PACK32, 4);
|
||||
fmt(A2R10G10B10_USCALED_PACK32, 4);
|
||||
fmt(A2R10G10B10_SSCALED_PACK32, 4);
|
||||
fmt(A2R10G10B10_UINT_PACK32, 4);
|
||||
fmt(A2R10G10B10_SINT_PACK32, 4);
|
||||
fmt(R16_UNORM, 2);
|
||||
fmt(R16_SNORM, 2);
|
||||
fmt(R16_USCALED, 2);
|
||||
fmt(R16_SSCALED, 2);
|
||||
fmt(R16_UINT, 2);
|
||||
fmt(R16_SINT, 2);
|
||||
fmt(R16_SFLOAT, 2);
|
||||
fmt(R16G16_UNORM, 4);
|
||||
fmt(R16G16_SNORM, 4);
|
||||
fmt(R16G16_USCALED, 4);
|
||||
fmt(R16G16_SSCALED, 4);
|
||||
fmt(R16G16_UINT, 4);
|
||||
fmt(R16G16_SINT, 4);
|
||||
fmt(R16G16_SFLOAT, 4);
|
||||
fmt(R16G16B16_UNORM, 6);
|
||||
fmt(R16G16B16_SNORM, 6);
|
||||
fmt(R16G16B16_USCALED, 6);
|
||||
fmt(R16G16B16_SSCALED, 6);
|
||||
fmt(R16G16B16_UINT, 6);
|
||||
fmt(R16G16B16_SINT, 6);
|
||||
fmt(R16G16B16_SFLOAT, 6);
|
||||
fmt(R16G16B16A16_UNORM, 8);
|
||||
fmt(R16G16B16A16_SNORM, 8);
|
||||
fmt(R16G16B16A16_USCALED, 8);
|
||||
fmt(R16G16B16A16_SSCALED, 8);
|
||||
fmt(R16G16B16A16_UINT, 8);
|
||||
fmt(R16G16B16A16_SINT, 8);
|
||||
fmt(R16G16B16A16_SFLOAT, 8);
|
||||
fmt(R32_UINT, 4);
|
||||
fmt(R32_SINT, 4);
|
||||
fmt(R32_SFLOAT, 4);
|
||||
fmt(R32G32_UINT, 8);
|
||||
fmt(R32G32_SINT, 8);
|
||||
fmt(R32G32_SFLOAT, 8);
|
||||
fmt(R32G32B32_UINT, 12);
|
||||
fmt(R32G32B32_SINT, 12);
|
||||
fmt(R32G32B32_SFLOAT, 12);
|
||||
fmt(R32G32B32A32_UINT, 16);
|
||||
fmt(R32G32B32A32_SINT, 16);
|
||||
fmt(R32G32B32A32_SFLOAT, 16);
|
||||
fmt(R64_UINT, 8);
|
||||
fmt(R64_SINT, 8);
|
||||
fmt(R64_SFLOAT, 8);
|
||||
fmt(R64G64_UINT, 16);
|
||||
fmt(R64G64_SINT, 16);
|
||||
fmt(R64G64_SFLOAT, 16);
|
||||
fmt(R64G64B64_UINT, 24);
|
||||
fmt(R64G64B64_SINT, 24);
|
||||
fmt(R64G64B64_SFLOAT, 24);
|
||||
fmt(R64G64B64A64_UINT, 32);
|
||||
fmt(R64G64B64A64_SINT, 32);
|
||||
fmt(R64G64B64A64_SFLOAT, 32);
|
||||
fmt(B10G11R11_UFLOAT_PACK32, 4);
|
||||
fmt(E5B9G9R9_UFLOAT_PACK32, 4);
|
||||
|
||||
fmt(D16_UNORM, 2);
|
||||
fmt(X8_D24_UNORM_PACK32, 4);
|
||||
fmt(D32_SFLOAT, 4);
|
||||
fmt(S8_UINT, 1);
|
||||
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
return aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? 2 : 1;
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return aspect == VK_IMAGE_ASPECT_DEPTH_BIT ? 4 : 1;
|
||||
|
||||
// ETC2
|
||||
fmt(ETC2_R8G8B8A8_UNORM_BLOCK, 16);
|
||||
fmt(ETC2_R8G8B8A8_SRGB_BLOCK, 16);
|
||||
fmt(ETC2_R8G8B8A1_UNORM_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8A1_SRGB_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8_UNORM_BLOCK, 8);
|
||||
fmt(ETC2_R8G8B8_SRGB_BLOCK, 8);
|
||||
fmt(EAC_R11_UNORM_BLOCK, 8);
|
||||
fmt(EAC_R11_SNORM_BLOCK, 8);
|
||||
fmt(EAC_R11G11_UNORM_BLOCK, 16);
|
||||
fmt(EAC_R11G11_SNORM_BLOCK, 16);
|
||||
|
||||
// BC
|
||||
fmt(BC1_RGB_UNORM_BLOCK, 8);
|
||||
fmt(BC1_RGB_SRGB_BLOCK, 8);
|
||||
fmt(BC1_RGBA_UNORM_BLOCK, 8);
|
||||
fmt(BC1_RGBA_SRGB_BLOCK, 8);
|
||||
fmt(BC2_UNORM_BLOCK, 16);
|
||||
fmt(BC2_SRGB_BLOCK, 16);
|
||||
fmt(BC3_UNORM_BLOCK, 16);
|
||||
fmt(BC3_SRGB_BLOCK, 16);
|
||||
fmt(BC4_UNORM_BLOCK, 8);
|
||||
fmt(BC4_SNORM_BLOCK, 8);
|
||||
fmt(BC5_UNORM_BLOCK, 16);
|
||||
fmt(BC5_SNORM_BLOCK, 16);
|
||||
fmt(BC6H_UFLOAT_BLOCK, 16);
|
||||
fmt(BC6H_SFLOAT_BLOCK, 16);
|
||||
fmt(BC7_SRGB_BLOCK, 16);
|
||||
fmt(BC7_UNORM_BLOCK, 16);
|
||||
|
||||
// ASTC
|
||||
#define astc_fmt(w, h) \
|
||||
fmt(ASTC_##w##x##h##_UNORM_BLOCK, 16); \
|
||||
fmt(ASTC_##w##x##h##_SRGB_BLOCK, 16); \
|
||||
fmt(ASTC_##w##x##h##_SFLOAT_BLOCK_EXT, 16)
|
||||
|
||||
astc_fmt(4, 4);
|
||||
astc_fmt(5, 4);
|
||||
astc_fmt(5, 5);
|
||||
astc_fmt(6, 5);
|
||||
astc_fmt(6, 6);
|
||||
astc_fmt(8, 5);
|
||||
astc_fmt(8, 6);
|
||||
astc_fmt(8, 8);
|
||||
astc_fmt(10, 5);
|
||||
astc_fmt(10, 6);
|
||||
astc_fmt(10, 8);
|
||||
astc_fmt(10, 10);
|
||||
astc_fmt(12, 10);
|
||||
astc_fmt(12, 12);
|
||||
|
||||
fmt(G8B8G8R8_422_UNORM, 4);
|
||||
fmt(B8G8R8G8_422_UNORM, 4);
|
||||
|
||||
fmt(G8_B8_R8_3PLANE_420_UNORM, 1);
|
||||
fmt2(G8_B8R8_2PLANE_420_UNORM, 1, 2);
|
||||
fmt(G8_B8_R8_3PLANE_422_UNORM, 1);
|
||||
fmt2(G8_B8R8_2PLANE_422_UNORM, 1, 2);
|
||||
fmt(G8_B8_R8_3PLANE_444_UNORM, 1);
|
||||
|
||||
fmt(R10X6_UNORM_PACK16, 2);
|
||||
fmt(R10X6G10X6_UNORM_2PACK16, 4);
|
||||
fmt(R10X6G10X6B10X6A10X6_UNORM_4PACK16, 8);
|
||||
fmt(G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, 8);
|
||||
fmt(B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, 8);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, 2);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, 2);
|
||||
fmt(G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, 2);
|
||||
fmt2(G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, 2, 4);
|
||||
fmt2(G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, 2, 4);
|
||||
|
||||
fmt(R12X4_UNORM_PACK16, 2);
|
||||
fmt(R12X4G12X4_UNORM_2PACK16, 4);
|
||||
fmt(R12X4G12X4B12X4A12X4_UNORM_4PACK16, 8);
|
||||
fmt(G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, 8);
|
||||
fmt(B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, 8);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, 2);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, 2);
|
||||
fmt(G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, 2);
|
||||
fmt2(G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, 2, 4);
|
||||
fmt2(G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, 2, 4);
|
||||
|
||||
fmt(G16B16G16R16_422_UNORM, 8);
|
||||
fmt(B16G16R16G16_422_UNORM, 8);
|
||||
fmt(G16_B16_R16_3PLANE_420_UNORM, 2);
|
||||
fmt(G16_B16_R16_3PLANE_422_UNORM, 2);
|
||||
fmt(G16_B16_R16_3PLANE_444_UNORM, 2);
|
||||
fmt2(G16_B16R16_2PLANE_420_UNORM, 2, 4);
|
||||
fmt2(G16_B16R16_2PLANE_422_UNORM, 2, 4);
|
||||
|
||||
default:
|
||||
assert(0 && "Unknown format.");
|
||||
return 0;
|
||||
}
|
||||
#undef fmt
|
||||
#undef fmt2
|
||||
#undef astc_fmt
|
||||
}
|
||||
|
||||
void TextureFormatLayout::fill_mipinfo(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
block_stride = format_block_size(format, 0);
|
||||
format_block_dim(format, block_dim_x, block_dim_y);
|
||||
|
||||
if (mip_levels == 0)
|
||||
mip_levels = num_miplevels(width, height, depth);
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
for (uint32_t mip = 0; mip < mip_levels; mip++)
|
||||
{
|
||||
offset = (offset + 15) & ~15;
|
||||
|
||||
uint32_t blocks_x = (width + block_dim_x - 1) / block_dim_x;
|
||||
uint32_t blocks_y = (height + block_dim_y - 1) / block_dim_y;
|
||||
size_t mip_size = blocks_x * blocks_y * array_layers * depth * block_stride;
|
||||
|
||||
mips[mip].offset = offset;
|
||||
|
||||
mips[mip].block_row_length = blocks_x;
|
||||
mips[mip].block_image_height = blocks_y;
|
||||
|
||||
mips[mip].row_length = blocks_x * block_dim_x;
|
||||
mips[mip].image_height = blocks_y * block_dim_y;
|
||||
|
||||
mips[mip].width = width;
|
||||
mips[mip].height = height;
|
||||
mips[mip].depth = depth;
|
||||
|
||||
offset += mip_size;
|
||||
|
||||
width = std::max((width >> 1u), 1u);
|
||||
height = std::max((height >> 1u), 1u);
|
||||
depth = std::max((depth >> 1u), 1u);
|
||||
}
|
||||
|
||||
required_size = offset;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_1d(VkFormat format_, uint32_t width, uint32_t array_layers_, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_1D;
|
||||
format = format_;
|
||||
array_layers = array_layers_;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, 1, 1);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_2d(VkFormat format_, uint32_t width, uint32_t height,
|
||||
uint32_t array_layers_, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_2D;
|
||||
format = format_;
|
||||
array_layers = array_layers_;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, height, 1);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_3d(VkFormat format_, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels_)
|
||||
{
|
||||
image_type = VK_IMAGE_TYPE_3D;
|
||||
format = format_;
|
||||
array_layers = 1;
|
||||
mip_levels = mip_levels_;
|
||||
|
||||
fill_mipinfo(width, height, depth);
|
||||
}
|
||||
|
||||
void TextureFormatLayout::set_buffer(void *buffer_, size_t size)
|
||||
{
|
||||
buffer = static_cast<uint8_t *>(buffer_);
|
||||
buffer_size = size;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_width(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].width;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_height(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].height;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_depth(uint32_t mip) const
|
||||
{
|
||||
return mips[mip].depth;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_layers() const
|
||||
{
|
||||
return array_layers;
|
||||
}
|
||||
|
||||
VkImageType TextureFormatLayout::get_image_type() const
|
||||
{
|
||||
return image_type;
|
||||
}
|
||||
|
||||
VkFormat TextureFormatLayout::get_format() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_stride() const
|
||||
{
|
||||
return block_stride;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_levels() const
|
||||
{
|
||||
return mip_levels;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::get_required_size() const
|
||||
{
|
||||
return required_size;
|
||||
}
|
||||
|
||||
const TextureFormatLayout::MipInfo &TextureFormatLayout::get_mip_info(uint32_t mip) const
|
||||
{
|
||||
return mips[mip];
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_dim_x() const
|
||||
{
|
||||
return block_dim_x;
|
||||
}
|
||||
|
||||
uint32_t TextureFormatLayout::get_block_dim_y() const
|
||||
{
|
||||
return block_dim_y;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::row_byte_stride(uint32_t row_length) const
|
||||
{
|
||||
return ((row_length + block_dim_x - 1) / block_dim_x) * block_stride;
|
||||
}
|
||||
|
||||
size_t TextureFormatLayout::layer_byte_stride(uint32_t image_height, size_t row_byte_stride) const
|
||||
{
|
||||
return ((image_height + block_dim_y - 1) / block_dim_y) * row_byte_stride;
|
||||
}
|
||||
|
||||
void TextureFormatLayout::build_buffer_image_copies(Util::SmallVector<VkBufferImageCopy, 32> &copies) const
|
||||
{
|
||||
copies.resize(mip_levels);
|
||||
for (unsigned level = 0; level < mip_levels; level++)
|
||||
{
|
||||
const auto &mip_info = mips[level];
|
||||
|
||||
auto &blit = copies[level];
|
||||
blit = {};
|
||||
blit.bufferOffset = mip_info.offset;
|
||||
blit.bufferRowLength = mip_info.row_length;
|
||||
blit.bufferImageHeight = mip_info.image_height;
|
||||
blit.imageSubresource.aspectMask = format_to_aspect_mask(format);
|
||||
blit.imageSubresource.mipLevel = level;
|
||||
blit.imageSubresource.baseArrayLayer = 0;
|
||||
blit.imageSubresource.layerCount = array_layers;
|
||||
blit.imageExtent.width = mip_info.width;
|
||||
blit.imageExtent.height = mip_info.height;
|
||||
blit.imageExtent.depth = mip_info.depth;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vulkan_headers.hpp"
|
||||
#include "small_vector.hpp"
|
||||
#include <vector>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class TextureFormatLayout
|
||||
{
|
||||
public:
|
||||
void set_1d(VkFormat format, uint32_t width, uint32_t array_layers = 1, uint32_t mip_levels = 1);
|
||||
void set_2d(VkFormat format, uint32_t width, uint32_t height, uint32_t array_layers = 1, uint32_t mip_levels = 1);
|
||||
void set_3d(VkFormat format, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels = 1);
|
||||
|
||||
static uint32_t format_block_size(VkFormat format, VkImageAspectFlags aspect);
|
||||
static void format_block_dim(VkFormat format, uint32_t &width, uint32_t &height);
|
||||
static uint32_t num_miplevels(uint32_t width, uint32_t height = 1, uint32_t depth = 1);
|
||||
|
||||
void set_buffer(void *buffer, size_t size);
|
||||
inline void *get_buffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint32_t get_width(uint32_t mip = 0) const;
|
||||
uint32_t get_height(uint32_t mip = 0) const;
|
||||
uint32_t get_depth(uint32_t mip = 0) const;
|
||||
uint32_t get_levels() const;
|
||||
uint32_t get_layers() const;
|
||||
uint32_t get_block_stride() const;
|
||||
uint32_t get_block_dim_x() const;
|
||||
uint32_t get_block_dim_y() const;
|
||||
VkImageType get_image_type() const;
|
||||
VkFormat get_format() const;
|
||||
|
||||
size_t get_required_size() const;
|
||||
|
||||
size_t row_byte_stride(uint32_t row_length) const;
|
||||
size_t layer_byte_stride(uint32_t row_length, size_t row_byte_stride) const;
|
||||
|
||||
inline size_t get_row_size(uint32_t mip) const
|
||||
{
|
||||
return size_t(mips[mip].block_row_length) * block_stride;
|
||||
}
|
||||
|
||||
inline size_t get_layer_size(uint32_t mip) const
|
||||
{
|
||||
return size_t(mips[mip].block_image_height) * get_row_size(mip);
|
||||
}
|
||||
|
||||
struct MipInfo
|
||||
{
|
||||
size_t offset = 0;
|
||||
uint32_t width = 1;
|
||||
uint32_t height = 1;
|
||||
uint32_t depth = 1;
|
||||
|
||||
uint32_t block_image_height = 0;
|
||||
uint32_t block_row_length = 0;
|
||||
uint32_t image_height = 0;
|
||||
uint32_t row_length = 0;
|
||||
};
|
||||
|
||||
const MipInfo &get_mip_info(uint32_t mip) const;
|
||||
|
||||
inline void *data(uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer_size == required_size);
|
||||
auto &mip_info = mips[mip];
|
||||
uint8_t *slice = buffer + mip_info.offset;
|
||||
slice += block_stride * layer * mip_info.block_row_length * mip_info.block_image_height;
|
||||
return slice;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_generic(uint32_t x, uint32_t y, uint32_t slice_index, uint32_t mip = 0) const
|
||||
{
|
||||
auto &mip_info = mips[mip];
|
||||
T *slice = reinterpret_cast<T *>(buffer + mip_info.offset);
|
||||
slice += slice_index * mip_info.block_row_length * mip_info.block_image_height;
|
||||
slice += y * mip_info.block_row_length;
|
||||
slice += x;
|
||||
return slice;
|
||||
}
|
||||
|
||||
inline void *data_opaque(uint32_t x, uint32_t y, uint32_t slice_index, uint32_t mip = 0) const
|
||||
{
|
||||
auto &mip_info = mips[mip];
|
||||
uint8_t *slice = buffer + mip_info.offset;
|
||||
size_t off = slice_index * mip_info.block_row_length * mip_info.block_image_height;
|
||||
off += y * mip_info.block_row_length;
|
||||
off += x;
|
||||
return slice + off * block_stride;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_generic() const
|
||||
{
|
||||
return data_generic<T>(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_1d(uint32_t x, uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_1D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, 0, layer, mip);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_2d(uint32_t x, uint32_t y, uint32_t layer = 0, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_2D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, y, layer, mip);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T *data_3d(uint32_t x, uint32_t y, uint32_t z, uint32_t mip = 0) const
|
||||
{
|
||||
assert(sizeof(T) == block_stride);
|
||||
assert(buffer);
|
||||
assert(image_type == VK_IMAGE_TYPE_3D);
|
||||
assert(buffer_size == required_size);
|
||||
return data_generic<T>(x, y, z, mip);
|
||||
}
|
||||
|
||||
void build_buffer_image_copies(Util::SmallVector<VkBufferImageCopy, 32> &copies) const;
|
||||
|
||||
private:
|
||||
uint8_t *buffer = nullptr;
|
||||
size_t buffer_size = 0;
|
||||
|
||||
VkImageType image_type = VK_IMAGE_TYPE_MAX_ENUM;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
size_t required_size = 0;
|
||||
|
||||
uint32_t block_stride = 1;
|
||||
uint32_t mip_levels = 1;
|
||||
uint32_t array_layers = 1;
|
||||
uint32_t block_dim_x = 1;
|
||||
uint32_t block_dim_y = 1;
|
||||
|
||||
MipInfo mips[16];
|
||||
|
||||
void fill_mipinfo(uint32_t width, uint32_t height, uint32_t depth);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user