Initial claude dump
This commit is contained in:
@@ -47,6 +47,27 @@ struct MeshComponent {
|
||||
MeshHandle handle;
|
||||
};
|
||||
|
||||
/**
|
||||
* A textured quad in the XY plane, placed by the entity's TransformComponent.
|
||||
* Only rotation around Z is meaningful.
|
||||
*/
|
||||
struct SpriteComponent {
|
||||
// Invalid draws a solid quad in `color`.
|
||||
TextureHandle texture = INVALID_HANDLE;
|
||||
glm::vec4 color{1.f, 1.f, 1.f, 1.f};
|
||||
|
||||
// Size in world units, before the transform's scale.
|
||||
glm::vec2 size{1.f, 1.f};
|
||||
// Point on the sprite, from (0, 0) to (1, 1) across its size, that sits at
|
||||
// the transform's position and that it rotates around.
|
||||
glm::vec2 pivot{0.5f, 0.5f};
|
||||
// Region of the texture to draw, as normalized (u0, v0, u1, v1).
|
||||
glm::vec4 uvRect{0.f, 0.f, 1.f, 1.f};
|
||||
|
||||
// Higher draws on top. Ties are broken by the transform's position.z.
|
||||
s32 sortOrder = 0;
|
||||
};
|
||||
|
||||
struct CameraComponent {
|
||||
struct Orthographic {
|
||||
float height = 10.f;
|
||||
|
||||
@@ -59,4 +59,11 @@ class Entity {
|
||||
Scene* scene = nullptr;
|
||||
};
|
||||
|
||||
template <typename Fn>
|
||||
void Scene::forEachEntity(Fn&& fn) {
|
||||
for (auto [handle] : registry.storage<entt::entity>().each()) {
|
||||
fn(Entity{handle, this});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -38,11 +38,13 @@ class Panel;
|
||||
class RenderTarget;
|
||||
class Scene;
|
||||
struct SceneView;
|
||||
class Texture;
|
||||
|
||||
using MeshHandle = Handle<Mesh>;
|
||||
using PanelHandle = Handle<Panel>;
|
||||
using RenderTargetHandle = Handle<RenderTarget>;
|
||||
using SceneHandle = Handle<Scene>;
|
||||
using SceneViewHandle = Handle<SceneView>;
|
||||
using TextureHandle = Handle<Texture>;
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -26,8 +26,10 @@ class ResourceRegistryRef {
|
||||
void requestResize(RenderTargetHandle handle, u32 width, u32 height);
|
||||
|
||||
glm::vec2 getExtent(RenderTargetHandle handle) const;
|
||||
glm::vec2 getExtent(TextureHandle handle) const;
|
||||
|
||||
bool isAlive(RenderTargetHandle handle) const;
|
||||
bool isAlive(TextureHandle handle) const;
|
||||
|
||||
template <typename T>
|
||||
void release(Handle<T> handle);
|
||||
@@ -45,16 +47,25 @@ class ResourceRegistryRef {
|
||||
RenderTargetHandle createResource(std::type_identity<RenderTarget>,
|
||||
u32 width = 1, u32 height = 1);
|
||||
|
||||
TextureHandle createResource(std::type_identity<Texture>, u32 width,
|
||||
u32 height, std::vector<u8> pixels);
|
||||
|
||||
ResourceRegistry* registry;
|
||||
};
|
||||
|
||||
template <>
|
||||
MeshHandle ResourceRegistryRef::load<Mesh>(const std::string& path);
|
||||
|
||||
template <>
|
||||
TextureHandle ResourceRegistryRef::load<Texture>(const std::string& path);
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Mesh>(MeshHandle handle);
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle);
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Texture>(TextureHandle handle);
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -13,9 +13,14 @@ class Scene {
|
||||
Scene();
|
||||
~Scene();
|
||||
|
||||
void createEntity(const std::string& name = {});
|
||||
// Every entity gets a TagComponent (named `name`) and a TransformComponent.
|
||||
Entity createEntity(const std::string& name = {});
|
||||
void destroyEntity(Entity entity);
|
||||
|
||||
/** Calls `fn(Entity)` for every live entity. Defined in entity.hpp. */
|
||||
template <typename Fn>
|
||||
void forEachEntity(Fn&& fn);
|
||||
|
||||
// The camera a render request uses when it doesn't name one. Null until
|
||||
// set.
|
||||
entt::entity getActiveCamera() const { return activeCamera; }
|
||||
|
||||
@@ -65,7 +65,8 @@ class Application::Impl {
|
||||
void run() {
|
||||
ModelRenderSystem modelRenderSystem{device, renderContext};
|
||||
SceneRenderer sceneRenderer{this->application.sceneRegistry,
|
||||
this->resourceRegistry};
|
||||
this->resourceRegistry, device,
|
||||
renderContext.getImageFormat()};
|
||||
ImGuiRenderer imguiRenderer{window, device, renderContext.getImageFormat(),
|
||||
renderContext.getImageCount(),
|
||||
this->resourceRegistry};
|
||||
@@ -128,6 +129,7 @@ class Application::Impl {
|
||||
}
|
||||
|
||||
imguiRenderer.beginFrame();
|
||||
sceneRenderer.beginFrame(renderContext.getFrameIndex());
|
||||
|
||||
panelRegistry.update(frameInfo);
|
||||
this->application.onRender();
|
||||
@@ -170,10 +172,11 @@ class Application::Impl {
|
||||
: vk::AttachmentLoadOp::eLoad,
|
||||
{color.r, color.g, color.b, color.a});
|
||||
},
|
||||
[&sceneRenderer, &sceneViewRegistry, handle](vk::CommandBuffer) {
|
||||
const SceneView& view = sceneViewRegistry.get(handle);
|
||||
[&sceneRenderer, &sceneViewRegistryConst,
|
||||
handle](vk::CommandBuffer commandBuffer) {
|
||||
const SceneView& view = sceneViewRegistryConst.get(handle);
|
||||
if (view.camera) {
|
||||
sceneRenderer.render(view);
|
||||
sceneRenderer.render(commandBuffer, view);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class RenderContext {
|
||||
vk::Format getImageFormat() const {
|
||||
return this->swapchain->getImageFormat();
|
||||
}
|
||||
u32 getFrameIndex() const { return this->currentFrameIndex; }
|
||||
u32 getImageCount() const {
|
||||
return static_cast<u32>(this->swapchain->imageCount());
|
||||
}
|
||||
|
||||
@@ -1,74 +1,102 @@
|
||||
#include "rendering/scene_renderer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sophia/components.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <tuple>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
struct PushConstantData {
|
||||
glm::mat4 transform{1.f};
|
||||
glm::vec4 color{1.f};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SceneRenderer::SceneRenderer(const SceneRegistry& sceneRegistry,
|
||||
ResourceRegistry& resources)
|
||||
: sceneRegistry{sceneRegistry}, resources{resources} {}
|
||||
ResourceRegistry& resources, Device& device,
|
||||
vk::Format colorFormat)
|
||||
: sceneRegistry{sceneRegistry},
|
||||
resources{resources},
|
||||
spriteRenderSystem{device, colorFormat, resources} {}
|
||||
|
||||
SceneRenderer::~SceneRenderer() {}
|
||||
|
||||
void SceneRenderer::render(SceneView sceneView) {
|
||||
auto& handle = sceneView.scene;
|
||||
auto& scene = this->sceneRegistry.get(handle);
|
||||
void SceneRenderer::beginFrame(u32 frameIndex) {
|
||||
this->spriteRenderSystem.beginFrame(frameIndex);
|
||||
}
|
||||
|
||||
auto& camera = sceneView.camera;
|
||||
SOPHIA_ASSERT(camera.hasComponent<CameraComponent>(),
|
||||
void SceneRenderer::render(vk::CommandBuffer commandBuffer,
|
||||
const SceneView& sceneView) {
|
||||
const Scene& scene = this->sceneRegistry.get(sceneView.scene);
|
||||
const entt::registry& registry = scene.registry;
|
||||
|
||||
entt::entity camera = sceneView.camera;
|
||||
SOPHIA_ASSERT(registry.all_of<CameraComponent>(camera),
|
||||
"camera does not have camera component!");
|
||||
|
||||
auto& registry = scene.registry;
|
||||
vk::Extent2D extent =
|
||||
this->resources.renderTargetPool.get(sceneView.target).getExtent();
|
||||
float aspect =
|
||||
static_cast<float>(extent.width) / static_cast<float>(extent.height);
|
||||
|
||||
auto view = registry.view<MeshComponent, TransformComponent>();
|
||||
// The view matrix ignores the camera's scale; zoom is the projection's job.
|
||||
glm::mat4 cameraWorld{1.f};
|
||||
if (const auto* transform = registry.try_get<TransformComponent>(camera)) {
|
||||
cameraWorld = glm::translate(glm::mat4{1.f}, transform->position) *
|
||||
glm::mat4_cast(transform->rotation);
|
||||
}
|
||||
glm::mat4 viewProjection =
|
||||
registry.get<CameraComponent>(camera).getProjection(aspect) *
|
||||
glm::inverse(cameraWorld);
|
||||
|
||||
for (auto entity : view) {
|
||||
auto& transform = view.get<TransformComponent>(entity);
|
||||
log::info(transform);
|
||||
renderSprites(commandBuffer, scene, viewProjection);
|
||||
|
||||
// TODO: meshes. Group MeshComponent entities by mesh and draw each group
|
||||
// (instanced), as sprites are grouped by texture.
|
||||
}
|
||||
|
||||
void SceneRenderer::renderSprites(vk::CommandBuffer commandBuffer,
|
||||
const Scene& scene,
|
||||
const glm::mat4& viewProjection) {
|
||||
std::vector<SpriteDrawItem>& items = this->spriteDrawItems;
|
||||
items.clear();
|
||||
|
||||
auto view =
|
||||
scene.registry.view<const TransformComponent, const SpriteComponent>();
|
||||
for (auto [entity, transform, sprite] : view.each()) {
|
||||
TextureHandle texture = this->resources.texturePool.isAlive(sprite.texture)
|
||||
? sprite.texture
|
||||
: this->resources.whiteTexture;
|
||||
|
||||
// Unit quad -> sprite rectangle around its pivot -> world.
|
||||
glm::mat4 model =
|
||||
transform.mat4() *
|
||||
glm::translate(glm::mat4{1.f},
|
||||
glm::vec3{-sprite.pivot * sprite.size, 0.f}) *
|
||||
glm::scale(glm::mat4{1.f}, glm::vec3{sprite.size, 1.f});
|
||||
|
||||
items.push_back({sprite.sortOrder,
|
||||
transform.position.z,
|
||||
texture,
|
||||
{model, sprite.color, sprite.uvRect}});
|
||||
}
|
||||
|
||||
// Get all mesh
|
||||
// For each mesh, get all entities(ES) that have a transform and that mesh
|
||||
// For e in ES: add e's transform component to a list
|
||||
// Texture is the last key so that sprites whose order doesn't matter
|
||||
// relative to each other still end up in as few batches as possible.
|
||||
std::ranges::sort(items, {}, [](const SpriteDrawItem& item) {
|
||||
return std::tuple{item.sortOrder, item.depth, item.texture};
|
||||
});
|
||||
|
||||
// We now have
|
||||
/*
|
||||
M = mesh
|
||||
T = transform
|
||||
this->spriteInstances.clear();
|
||||
this->spriteBatches.clear();
|
||||
for (const SpriteDrawItem& item : items) {
|
||||
auto index = static_cast<u32>(this->spriteInstances.size());
|
||||
this->spriteInstances.push_back(item.instance);
|
||||
|
||||
Work Batch
|
||||
|- M0
|
||||
| |- T0
|
||||
| \- T2
|
||||
|
|
||||
|- M1
|
||||
| |- T1
|
||||
| \- T5
|
||||
|
|
||||
\- M2
|
||||
|- T3
|
||||
\- T4
|
||||
if (this->spriteBatches.empty() ||
|
||||
this->spriteBatches.back().texture != item.texture) {
|
||||
this->spriteBatches.push_back({item.texture, index, 0});
|
||||
}
|
||||
this->spriteBatches.back().instanceCount++;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
for batch in Work Batch:
|
||||
batch.mesh.bind()
|
||||
|
||||
for transform in batch.transforms:
|
||||
commandBuffer.pushConstants(transform)
|
||||
batch.mesh.draw(commandBuffer)
|
||||
|
||||
NOTE: Use instanced rendering?
|
||||
*/
|
||||
this->spriteRenderSystem.render(commandBuffer, viewProjection,
|
||||
this->spriteInstances, this->spriteBatches);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -2,28 +2,54 @@
|
||||
|
||||
#include <sophia/scene_registry.hpp>
|
||||
#include <sophia/scene_view.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "rendering/sprite_render_system.hpp"
|
||||
#include "resources/resource_registry.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
/**
|
||||
* !TEMP to be decoupled once scene batching and rendering impl
|
||||
*
|
||||
* Turns a scene's components into draw calls for a SceneView.
|
||||
*/
|
||||
class SceneRenderer {
|
||||
public:
|
||||
SceneRenderer(const SceneRenderer&) = delete;
|
||||
SceneRenderer& operator=(const SceneRenderer&) = delete;
|
||||
|
||||
SceneRenderer(const SceneRegistry& sceneRegistry,
|
||||
ResourceRegistry& resources);
|
||||
SceneRenderer(const SceneRegistry& sceneRegistry, ResourceRegistry& resources,
|
||||
Device& device, vk::Format colorFormat);
|
||||
~SceneRenderer();
|
||||
|
||||
void render(SceneView sceneView);
|
||||
// Call once per frame, before any render().
|
||||
void beginFrame(u32 frameIndex);
|
||||
|
||||
// Records `sceneView` into `commandBuffer`. Rendering into the view's
|
||||
// target must already have begun.
|
||||
void render(vk::CommandBuffer commandBuffer, const SceneView& sceneView);
|
||||
|
||||
private:
|
||||
struct SpriteDrawItem {
|
||||
s32 sortOrder;
|
||||
float depth;
|
||||
TextureHandle texture;
|
||||
SpriteRenderSystem::Instance instance;
|
||||
};
|
||||
|
||||
void renderSprites(vk::CommandBuffer commandBuffer, const Scene& scene,
|
||||
const glm::mat4& viewProjection);
|
||||
|
||||
const SceneRegistry& sceneRegistry;
|
||||
ResourceRegistry& resources;
|
||||
|
||||
SpriteRenderSystem spriteRenderSystem;
|
||||
|
||||
// Reused across frames to avoid reallocating.
|
||||
std::vector<SpriteDrawItem> spriteDrawItems;
|
||||
std::vector<SpriteRenderSystem::Instance> spriteInstances;
|
||||
std::vector<SpriteRenderSystem::Batch> spriteBatches;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "rendering/sprite_render_system.hpp"
|
||||
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <sophia/util/logger.hpp>
|
||||
|
||||
#include "shaders/sprite.frag.spv.h"
|
||||
#include "shaders/sprite.vert.spv.h"
|
||||
#include "vulkan/descriptors/descriptor_writer.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
namespace {
|
||||
constexpr u32 MAX_TEXTURES = 1024;
|
||||
constexpr u32 VERTICES_PER_SPRITE = 6;
|
||||
} // namespace
|
||||
|
||||
SpriteRenderSystem::SpriteRenderSystem(Device& device, vk::Format colorFormat,
|
||||
ResourceRegistry& resources)
|
||||
: device{device}, resources{resources} {
|
||||
createDescriptors();
|
||||
createPipelineLayout();
|
||||
createPipeline(colorFormat);
|
||||
createInstanceBuffers();
|
||||
}
|
||||
|
||||
SpriteRenderSystem::~SpriteRenderSystem() {
|
||||
log::trace("destroyed sophia::SpriteRenderSystem");
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::beginFrame(u32 frameIndex) {
|
||||
this->frameIndex = frameIndex;
|
||||
this->instanceCursor = 0;
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::render(vk::CommandBuffer commandBuffer,
|
||||
const glm::mat4& viewProjection,
|
||||
std::span<const Instance> instances,
|
||||
std::span<const Batch> batches) {
|
||||
if (instances.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 available = MAX_INSTANCES_PER_FRAME - this->instanceCursor;
|
||||
if (instances.size() > available) {
|
||||
log::warning("SpriteRenderSystem: frame instance buffer full, dropping",
|
||||
instances.size() - available, "sprites");
|
||||
}
|
||||
|
||||
Buffer& buffer = *this->instanceBuffers[this->frameIndex];
|
||||
u32 base = this->instanceCursor;
|
||||
u32 count = std::min(static_cast<u32>(instances.size()), available);
|
||||
buffer.writeToBuffer(const_cast<Instance*>(instances.data()),
|
||||
count * sizeof(Instance), base * sizeof(Instance));
|
||||
this->instanceCursor += count;
|
||||
|
||||
this->graphicsPipeline->bind(commandBuffer);
|
||||
|
||||
PushConstantData push{viewProjection};
|
||||
commandBuffer.pushConstants(this->pipelineLayout.get(),
|
||||
vk::ShaderStageFlagBits::eVertex, 0,
|
||||
sizeof(PushConstantData), &push);
|
||||
|
||||
vk::Buffer vertexBuffer = buffer.getBuffer();
|
||||
vk::DeviceSize offset = 0;
|
||||
commandBuffer.bindVertexBuffers(0, 1, &vertexBuffer, &offset);
|
||||
|
||||
for (const Batch& batch : batches) {
|
||||
if (batch.firstInstance >= count) {
|
||||
break;
|
||||
}
|
||||
u32 batchCount = std::min(batch.instanceCount, count - batch.firstInstance);
|
||||
|
||||
vk::DescriptorSet set = getDescriptorSet(batch.texture);
|
||||
commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
this->pipelineLayout.get(), 0, 1, &set, 0,
|
||||
nullptr);
|
||||
commandBuffer.draw(VERTICES_PER_SPRITE, batchCount, 0,
|
||||
base + batch.firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorSet SpriteRenderSystem::getDescriptorSet(TextureHandle handle) {
|
||||
auto it = this->descriptorSets.find(handle.index);
|
||||
if (it != this->descriptorSets.end() &&
|
||||
it->second.generation == handle.generation) {
|
||||
return it->second.set;
|
||||
}
|
||||
|
||||
const Texture& texture = this->resources.texturePool.get(handle);
|
||||
vk::DescriptorImageInfo imageInfo = texture.descriptorInfo();
|
||||
DescriptorWriter writer{*this->setLayout, *this->descriptorPool};
|
||||
writer.writeImage(0, &imageInfo);
|
||||
|
||||
if (it == this->descriptorSets.end()) {
|
||||
vk::DescriptorSet set;
|
||||
bool allocated = writer.build(set);
|
||||
SOPHIA_ASSERT(allocated, "SpriteRenderSystem: out of texture descriptors");
|
||||
this->descriptorSets.emplace(static_cast<u32>(handle.index),
|
||||
CachedDescriptorSet{handle.generation, set});
|
||||
return set;
|
||||
}
|
||||
|
||||
// The slot was reused by a new texture. Releasing a texture waits for the
|
||||
// device to go idle, so no in-flight frame can still be using this set.
|
||||
writer.overwrite(it->second.set);
|
||||
it->second.generation = handle.generation;
|
||||
return it->second.set;
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::createDescriptors() {
|
||||
this->setLayout =
|
||||
DescriptorSetLayout::Builder(this->device)
|
||||
.addBinding(0, vk::DescriptorType::eCombinedImageSampler,
|
||||
vk::ShaderStageFlagBits::eFragment)
|
||||
.build();
|
||||
|
||||
this->descriptorPool =
|
||||
DescriptorPool::Builder(this->device)
|
||||
.setMaxSets(MAX_TEXTURES)
|
||||
.addPoolSize(vk::DescriptorType::eCombinedImageSampler, MAX_TEXTURES)
|
||||
.build();
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::createPipelineLayout() {
|
||||
vk::PushConstantRange pushConstantRange{};
|
||||
pushConstantRange.stageFlags = vk::ShaderStageFlagBits::eVertex;
|
||||
pushConstantRange.offset = 0;
|
||||
pushConstantRange.size = sizeof(PushConstantData);
|
||||
|
||||
vk::DescriptorSetLayout setLayout = this->setLayout->getDescriptorSetLayout();
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipelineLayoutInfo = {};
|
||||
pipelineLayoutInfo.setLayoutCount = 1;
|
||||
pipelineLayoutInfo.pSetLayouts = &setLayout;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
||||
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
|
||||
|
||||
try {
|
||||
this->pipelineLayout =
|
||||
this->device.get()->createPipelineLayoutUnique(pipelineLayoutInfo);
|
||||
log::trace("created vk::PipelineLayout");
|
||||
} catch (const vk::SystemError& err) {
|
||||
log::fatal("failed to create vk::PipelineLayout");
|
||||
throw std::runtime_error("failed to create vk::PipelineLayout");
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::createPipeline(vk::Format colorFormat) {
|
||||
auto vertexShader = Shader::Builder()
|
||||
.fromSPIRV(shaders::sprite_vert_spv)
|
||||
.setStage(ShaderStage::VERTEX)
|
||||
.build();
|
||||
auto fragmentShader = Shader::Builder()
|
||||
.fromSPIRV(shaders::sprite_frag_spv)
|
||||
.setStage(ShaderStage::FRAGMENT)
|
||||
.build();
|
||||
|
||||
std::vector<vk::VertexInputBindingDescription> bindings{
|
||||
{0, sizeof(Instance), vk::VertexInputRate::eInstance}};
|
||||
|
||||
std::vector<vk::VertexInputAttributeDescription> attributes;
|
||||
// A mat4 attribute takes four consecutive locations, one per column.
|
||||
for (u32 column = 0; column < 4; column++) {
|
||||
attributes.push_back({column, 0, vk::Format::eR32G32B32A32Sfloat,
|
||||
static_cast<u32>(offsetof(Instance, model) +
|
||||
column * sizeof(glm::vec4))});
|
||||
}
|
||||
attributes.push_back({4, 0, vk::Format::eR32G32B32A32Sfloat,
|
||||
static_cast<u32>(offsetof(Instance, color))});
|
||||
attributes.push_back({5, 0, vk::Format::eR32G32B32A32Sfloat,
|
||||
static_cast<u32>(offsetof(Instance, uvRect))});
|
||||
|
||||
this->graphicsPipeline = GraphicsPipeline::Builder(this->device)
|
||||
.setVertexShader(*vertexShader)
|
||||
.setFragmentShader(*fragmentShader)
|
||||
.setPipelineLayout(this->pipelineLayout.get())
|
||||
.setColorAttachmentFormats({colorFormat})
|
||||
.setVertexInput(bindings, attributes)
|
||||
.setAlphaBlending()
|
||||
.build();
|
||||
}
|
||||
|
||||
void SpriteRenderSystem::createInstanceBuffers() {
|
||||
for (std::unique_ptr<Buffer>& buffer : this->instanceBuffers) {
|
||||
buffer = std::make_unique<Buffer>(
|
||||
this->device, sizeof(Instance), MAX_INSTANCES_PER_FRAME,
|
||||
vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible |
|
||||
vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||
buffer->map();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/math.hpp>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rendering/render_context.hpp"
|
||||
#include "resources/resource_registry.hpp"
|
||||
#include "vulkan/buffer.hpp"
|
||||
#include "vulkan/descriptors/descriptor_pool.hpp"
|
||||
#include "vulkan/descriptors/descriptor_set_layout.hpp"
|
||||
#include "vulkan/device.hpp"
|
||||
#include "vulkan/pipelines/graphics_pipeline.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
/**
|
||||
* Draws batches of textured quads. Knows nothing about scenes: callers hand
|
||||
* it instances already in draw order, grouped into runs that share a texture.
|
||||
*
|
||||
* Instances are written to a per-frame host-visible buffer, so every
|
||||
* render() call within one frame appends to it; call beginFrame() once per
|
||||
* frame before any render().
|
||||
*/
|
||||
class SpriteRenderSystem {
|
||||
public:
|
||||
// Layout must match the per-instance inputs in sprite.vert.
|
||||
struct Instance {
|
||||
glm::mat4 model{1.f};
|
||||
glm::vec4 color{1.f};
|
||||
glm::vec4 uvRect{0.f, 0.f, 1.f, 1.f};
|
||||
};
|
||||
|
||||
// A run of consecutive instances drawn with the same texture.
|
||||
struct Batch {
|
||||
TextureHandle texture;
|
||||
u32 firstInstance;
|
||||
u32 instanceCount;
|
||||
};
|
||||
|
||||
static constexpr u32 MAX_INSTANCES_PER_FRAME = 32768;
|
||||
|
||||
SpriteRenderSystem(const SpriteRenderSystem&) = delete;
|
||||
SpriteRenderSystem& operator=(const SpriteRenderSystem&) = delete;
|
||||
|
||||
SpriteRenderSystem(Device& device, vk::Format colorFormat,
|
||||
ResourceRegistry& resources);
|
||||
~SpriteRenderSystem();
|
||||
|
||||
void beginFrame(u32 frameIndex);
|
||||
|
||||
void render(vk::CommandBuffer commandBuffer, const glm::mat4& viewProjection,
|
||||
std::span<const Instance> instances,
|
||||
std::span<const Batch> batches);
|
||||
|
||||
private:
|
||||
struct PushConstantData {
|
||||
glm::mat4 viewProjection{1.f};
|
||||
};
|
||||
|
||||
struct CachedDescriptorSet {
|
||||
u32 generation;
|
||||
vk::DescriptorSet set;
|
||||
};
|
||||
|
||||
void createDescriptors();
|
||||
void createPipelineLayout();
|
||||
void createPipeline(vk::Format colorFormat);
|
||||
void createInstanceBuffers();
|
||||
|
||||
vk::DescriptorSet getDescriptorSet(TextureHandle handle);
|
||||
|
||||
Device& device;
|
||||
ResourceRegistry& resources;
|
||||
|
||||
std::unique_ptr<DescriptorSetLayout> setLayout;
|
||||
std::unique_ptr<DescriptorPool> descriptorPool;
|
||||
// One set per texture slot, keyed by handle index. A generation mismatch
|
||||
// means the slot was reused by a new texture, so the set is rewritten.
|
||||
std::unordered_map<u32, CachedDescriptorSet> descriptorSets;
|
||||
|
||||
vk::UniquePipelineLayout pipelineLayout;
|
||||
std::unique_ptr<GraphicsPipeline> graphicsPipeline;
|
||||
|
||||
std::array<std::unique_ptr<Buffer>, RenderContext::MAX_FRAMES_IN_FLIGHT>
|
||||
instanceBuffers;
|
||||
u32 frameIndex = 0;
|
||||
u32 instanceCursor = 0;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/util/pool.hpp>
|
||||
|
||||
#include "resources/mesh.hpp"
|
||||
#include "resources/pools/render_target_pool.hpp"
|
||||
#include "resources/texture.hpp"
|
||||
#include "vulkan/device.hpp"
|
||||
|
||||
namespace sophia {
|
||||
@@ -15,13 +17,20 @@ class ResourceRegistry {
|
||||
ResourceRegistry& operator=(const ResourceRegistry&) = delete;
|
||||
|
||||
ResourceRegistry(Device& device, vk::Format renderTargetFormat)
|
||||
: device{device}, renderTargetPool{device, renderTargetFormat} {}
|
||||
: device{device}, renderTargetPool{device, renderTargetFormat} {
|
||||
constexpr std::array<u8, 4> WHITE{0xFF, 0xFF, 0xFF, 0xFF};
|
||||
whiteTexture = texturePool.create(device, 1, 1, WHITE);
|
||||
}
|
||||
|
||||
Device& device;
|
||||
Pool<Mesh> meshPool{"Mesh"};
|
||||
RenderTargetPool renderTargetPool;
|
||||
// TexturePool textures;
|
||||
Pool<Texture> texturePool{"Texture"};
|
||||
// MaterialPool materials;
|
||||
|
||||
// 1x1 white. Stands in for sprites without a texture, so they draw as a
|
||||
// solid quad in their tint color.
|
||||
TextureHandle whiteTexture = INVALID_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <sophia/resource_registry_ref.hpp>
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <sophia/util/logger.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "resources/resource_registry.hpp"
|
||||
|
||||
@@ -14,6 +18,26 @@ MeshHandle ResourceRegistryRef::load<Mesh>(const std::string& path) {
|
||||
return MeshHandle{};
|
||||
}
|
||||
|
||||
template <>
|
||||
TextureHandle ResourceRegistryRef::load<Texture>(const std::string& path) {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int channels = 0;
|
||||
// Always decode to RGBA8, whatever the file has.
|
||||
stbi_uc* pixels = stbi_load(path.c_str(), &width, &height, &channels, 4);
|
||||
if (pixels == nullptr) {
|
||||
log::fatal("failed to load texture", path, "-", stbi_failure_reason());
|
||||
throw std::runtime_error("failed to load texture: " + path);
|
||||
}
|
||||
|
||||
auto size = static_cast<size_t>(width) * static_cast<size_t>(height) * 4;
|
||||
TextureHandle handle = registry->texturePool.create(
|
||||
registry->device, static_cast<u32>(width), static_cast<u32>(height),
|
||||
std::span<const u8>{pixels, size});
|
||||
stbi_image_free(pixels);
|
||||
return handle;
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Mesh>(MeshHandle handle) {
|
||||
registry->meshPool.release(handle);
|
||||
@@ -24,6 +48,15 @@ void ResourceRegistryRef::release<RenderTarget>(RenderTargetHandle handle) {
|
||||
registry->renderTargetPool.release(handle);
|
||||
}
|
||||
|
||||
template <>
|
||||
void ResourceRegistryRef::release<Texture>(TextureHandle handle) {
|
||||
SOPHIA_ASSERT(handle != registry->whiteTexture,
|
||||
"the built-in white texture cannot be released");
|
||||
// Frames in flight may still be sampling it.
|
||||
registry->device.waitIdle();
|
||||
registry->texturePool.release(handle);
|
||||
}
|
||||
|
||||
MeshHandle ResourceRegistryRef::createResource(
|
||||
std::type_identity<Mesh>, std::vector<CanvasVertex> vertices,
|
||||
std::vector<u32> indices) {
|
||||
@@ -42,6 +75,13 @@ RenderTargetHandle ResourceRegistryRef::createResource(
|
||||
return registry->renderTargetPool.create(width, height);
|
||||
}
|
||||
|
||||
TextureHandle ResourceRegistryRef::createResource(std::type_identity<Texture>,
|
||||
u32 width, u32 height,
|
||||
std::vector<u8> pixels) {
|
||||
return registry->texturePool.create(registry->device, width, height,
|
||||
std::span<const u8>{pixels});
|
||||
}
|
||||
|
||||
void ResourceRegistryRef::requestResize(RenderTargetHandle handle, u32 width,
|
||||
u32 height) {
|
||||
registry->renderTargetPool.get(handle).requestResize(width, height);
|
||||
@@ -56,4 +96,14 @@ bool ResourceRegistryRef::isAlive(RenderTargetHandle handle) const {
|
||||
return registry->renderTargetPool.isAlive(handle);
|
||||
}
|
||||
|
||||
glm::vec2 ResourceRegistryRef::getExtent(TextureHandle handle) const {
|
||||
const Texture& texture = registry->texturePool.get(handle);
|
||||
return {static_cast<float>(texture.getWidth()),
|
||||
static_cast<float>(texture.getHeight())};
|
||||
}
|
||||
|
||||
bool ResourceRegistryRef::isAlive(TextureHandle handle) const {
|
||||
return registry->texturePool.isAlive(handle);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "resources/texture.hpp"
|
||||
|
||||
#include <sophia/util/assert.hpp>
|
||||
#include <sophia/util/logger.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
Texture::Texture(Device& device, u32 width, u32 height,
|
||||
std::span<const u8> pixels) {
|
||||
SOPHIA_ASSERT(width > 0 && height > 0, "texture must not be empty");
|
||||
SOPHIA_ASSERT(pixels.size() == static_cast<size_t>(width) * height * 4,
|
||||
"texture pixel data must be width * height RGBA8 texels");
|
||||
|
||||
this->image = Image::Builder(device)
|
||||
.setExtent(width, height)
|
||||
.setFormat(vk::Format::eR8G8B8A8Srgb)
|
||||
.setUsage(vk::ImageUsageFlagBits::eSampled)
|
||||
.setInitialData(pixels.data(), pixels.size())
|
||||
.withView()
|
||||
.withSampler(vk::Filter::eLinear,
|
||||
vk::SamplerAddressMode::eClampToEdge)
|
||||
.build();
|
||||
}
|
||||
|
||||
Texture::~Texture() { log::trace("destroyed sophia::Texture"); }
|
||||
|
||||
vk::DescriptorImageInfo Texture::descriptorInfo() const {
|
||||
return {this->image->getSampler(), this->image->getView(),
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sophia/types.hpp>
|
||||
#include <span>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "vulkan/device.hpp"
|
||||
#include "vulkan/image.hpp"
|
||||
|
||||
namespace sophia {
|
||||
|
||||
/**
|
||||
* A sampled, read-only RGBA8 (sRGB) image.
|
||||
*
|
||||
* Owned by the ResourceRegistry and addressed through a TextureHandle.
|
||||
*/
|
||||
class Texture {
|
||||
public:
|
||||
/** `pixels` is width * height tightly packed RGBA8 texels, top row first. */
|
||||
Texture(Device& device, u32 width, u32 height, std::span<const u8> pixels);
|
||||
~Texture();
|
||||
|
||||
Texture(const Texture&) = delete;
|
||||
Texture& operator=(const Texture&) = delete;
|
||||
|
||||
const Image& getImage() const { return *this->image; }
|
||||
|
||||
u32 getWidth() const { return this->image->getExtent().width; }
|
||||
u32 getHeight() const { return this->image->getExtent().height; }
|
||||
|
||||
vk::DescriptorImageInfo descriptorInfo() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Image> image;
|
||||
};
|
||||
|
||||
} // namespace sophia
|
||||
@@ -1,16 +1,26 @@
|
||||
#include <sophia/components.hpp>
|
||||
#include <sophia/entity.hpp>
|
||||
#include <sophia/scene.hpp>
|
||||
#include <sophia/util/logger.hpp>
|
||||
|
||||
namespace sophia {
|
||||
|
||||
Scene::Scene() {
|
||||
entt::entity entity = this->registry.create();
|
||||
|
||||
auto& a = this->registry.emplace<TransformComponent>(entity);
|
||||
auto& b = this->registry.get<TransformComponent>(entity);
|
||||
}
|
||||
Scene::Scene() {}
|
||||
|
||||
Scene::~Scene() { log::trace("destroying sophia::Scene"); }
|
||||
|
||||
Entity Scene::createEntity(const std::string& name) {
|
||||
Entity entity{this->registry.create(), this};
|
||||
entity.addComponent<TagComponent>(name.empty() ? "Entity" : name);
|
||||
entity.addComponent<TransformComponent>();
|
||||
return entity;
|
||||
}
|
||||
|
||||
void Scene::destroyEntity(Entity entity) {
|
||||
if (this->activeCamera == entity) {
|
||||
this->activeCamera = entt::null;
|
||||
}
|
||||
this->registry.destroy(entity);
|
||||
}
|
||||
|
||||
} // namespace sophia
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec4 fragColor;
|
||||
layout (location = 1) in vec2 fragUv;
|
||||
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
layout (set = 0, binding = 0) uniform sampler2D spriteTexture;
|
||||
|
||||
void main() {
|
||||
outColor = texture(spriteTexture, fragUv) * fragColor;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace sophia::shaders {
|
||||
|
||||
inline constexpr std::array<uint32_t, 186> sprite_frag_spv = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x00000018, 0x00000000, 0x00020011,
|
||||
0x00000001, 0x0006000b, 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001, 0x0008000f, 0x00000004,
|
||||
0x00000004, 0x6e69616d, 0x00000000, 0x00000009, 0x00000011, 0x00000015,
|
||||
0x00030010, 0x00000004, 0x00000007, 0x00030003, 0x00000002, 0x000001c2,
|
||||
0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70, 0x5f656c79,
|
||||
0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004, 0x475f4c47,
|
||||
0x4c474f4f, 0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572, 0x00657669,
|
||||
0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00050005, 0x00000009,
|
||||
0x4374756f, 0x726f6c6f, 0x00000000, 0x00060005, 0x0000000d, 0x69727073,
|
||||
0x65546574, 0x72757478, 0x00000065, 0x00040005, 0x00000011, 0x67617266,
|
||||
0x00007655, 0x00050005, 0x00000015, 0x67617266, 0x6f6c6f43, 0x00000072,
|
||||
0x00040047, 0x00000009, 0x0000001e, 0x00000000, 0x00040047, 0x0000000d,
|
||||
0x00000021, 0x00000000, 0x00040047, 0x0000000d, 0x00000022, 0x00000000,
|
||||
0x00040047, 0x00000011, 0x0000001e, 0x00000001, 0x00040047, 0x00000015,
|
||||
0x0000001e, 0x00000000, 0x00020013, 0x00000002, 0x00030021, 0x00000003,
|
||||
0x00000002, 0x00030016, 0x00000006, 0x00000020, 0x00040017, 0x00000007,
|
||||
0x00000006, 0x00000004, 0x00040020, 0x00000008, 0x00000003, 0x00000007,
|
||||
0x0004003b, 0x00000008, 0x00000009, 0x00000003, 0x00090019, 0x0000000a,
|
||||
0x00000006, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
||||
0x00000000, 0x0003001b, 0x0000000b, 0x0000000a, 0x00040020, 0x0000000c,
|
||||
0x00000000, 0x0000000b, 0x0004003b, 0x0000000c, 0x0000000d, 0x00000000,
|
||||
0x00040017, 0x0000000f, 0x00000006, 0x00000002, 0x00040020, 0x00000010,
|
||||
0x00000001, 0x0000000f, 0x0004003b, 0x00000010, 0x00000011, 0x00000001,
|
||||
0x00040020, 0x00000014, 0x00000001, 0x00000007, 0x0004003b, 0x00000014,
|
||||
0x00000015, 0x00000001, 0x00050036, 0x00000002, 0x00000004, 0x00000000,
|
||||
0x00000003, 0x000200f8, 0x00000005, 0x0004003d, 0x0000000b, 0x0000000e,
|
||||
0x0000000d, 0x0004003d, 0x0000000f, 0x00000012, 0x00000011, 0x00050057,
|
||||
0x00000007, 0x00000013, 0x0000000e, 0x00000012, 0x0004003d, 0x00000007,
|
||||
0x00000016, 0x00000015, 0x00050085, 0x00000007, 0x00000017, 0x00000013,
|
||||
0x00000016, 0x0003003e, 0x00000009, 0x00000017, 0x000100fd, 0x00010038,
|
||||
};
|
||||
|
||||
} // namespace sophia::shaders
|
||||
@@ -0,0 +1,31 @@
|
||||
#version 450
|
||||
|
||||
// Per instance; layout must match SpriteRenderSystem::Instance.
|
||||
layout (location = 0) in mat4 model; // Maps the unit quad to world space.
|
||||
layout (location = 4) in vec4 color;
|
||||
layout (location = 5) in vec4 uvRect;
|
||||
|
||||
layout (location = 0) out vec4 fragColor;
|
||||
layout (location = 1) out vec2 fragUv;
|
||||
|
||||
layout (push_constant) uniform Push {
|
||||
mat4 viewProjection;
|
||||
} push;
|
||||
|
||||
// Two triangles covering the unit quad (0,0)-(1,1).
|
||||
const vec2 CORNERS[6] = vec2[](
|
||||
vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0),
|
||||
vec2(1.0, 1.0), vec2(0.0, 1.0), vec2(0.0, 0.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
vec2 corner = CORNERS[gl_VertexIndex];
|
||||
|
||||
gl_Position = push.viewProjection * model * vec4(corner, 0.0, 1.0);
|
||||
// Draw order comes from the CPU-side sort, so flatten depth; sprites should
|
||||
// never be clipped by the camera's near/far planes.
|
||||
gl_Position.z = 0.0;
|
||||
|
||||
fragColor = color;
|
||||
fragUv = mix(uvRect.xy, uvRect.zw, corner);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace sophia::shaders {
|
||||
|
||||
inline constexpr std::array<uint32_t, 519> sprite_vert_spv = {
|
||||
0x07230203, 0x00010000, 0x000d000b, 0x00000045, 0x00000000, 0x00020011,
|
||||
0x00000001, 0x0006000b, 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
|
||||
0x00000000, 0x0003000e, 0x00000000, 0x00000001, 0x000c000f, 0x00000000,
|
||||
0x00000004, 0x6e69616d, 0x00000000, 0x00000016, 0x00000021, 0x0000002b,
|
||||
0x00000038, 0x0000003a, 0x0000003d, 0x0000003e, 0x00030003, 0x00000002,
|
||||
0x000001c2, 0x000a0004, 0x475f4c47, 0x4c474f4f, 0x70635f45, 0x74735f70,
|
||||
0x5f656c79, 0x656e696c, 0x7269645f, 0x69746365, 0x00006576, 0x00080004,
|
||||
0x475f4c47, 0x4c474f4f, 0x6e695f45, 0x64756c63, 0x69645f65, 0x74636572,
|
||||
0x00657669, 0x00040005, 0x00000004, 0x6e69616d, 0x00000000, 0x00040005,
|
||||
0x00000009, 0x6e726f63, 0x00007265, 0x00060005, 0x00000016, 0x565f6c67,
|
||||
0x65747265, 0x646e4978, 0x00007865, 0x00050005, 0x00000019, 0x65646e69,
|
||||
0x6c626178, 0x00000065, 0x00060005, 0x0000001f, 0x505f6c67, 0x65567265,
|
||||
0x78657472, 0x00000000, 0x00060006, 0x0000001f, 0x00000000, 0x505f6c67,
|
||||
0x7469736f, 0x006e6f69, 0x00070006, 0x0000001f, 0x00000001, 0x505f6c67,
|
||||
0x746e696f, 0x657a6953, 0x00000000, 0x00070006, 0x0000001f, 0x00000002,
|
||||
0x435f6c67, 0x4470696c, 0x61747369, 0x0065636e, 0x00070006, 0x0000001f,
|
||||
0x00000003, 0x435f6c67, 0x446c6c75, 0x61747369, 0x0065636e, 0x00030005,
|
||||
0x00000021, 0x00000000, 0x00040005, 0x00000024, 0x68737550, 0x00000000,
|
||||
0x00070006, 0x00000024, 0x00000000, 0x77656976, 0x6a6f7250, 0x69746365,
|
||||
0x00006e6f, 0x00040005, 0x00000026, 0x68737570, 0x00000000, 0x00040005,
|
||||
0x0000002b, 0x65646f6d, 0x0000006c, 0x00050005, 0x00000038, 0x67617266,
|
||||
0x6f6c6f43, 0x00000072, 0x00040005, 0x0000003a, 0x6f6c6f63, 0x00000072,
|
||||
0x00040005, 0x0000003d, 0x67617266, 0x00007655, 0x00040005, 0x0000003e,
|
||||
0x65527675, 0x00007463, 0x00040047, 0x00000016, 0x0000000b, 0x0000002a,
|
||||
0x00030047, 0x0000001f, 0x00000002, 0x00050048, 0x0000001f, 0x00000000,
|
||||
0x0000000b, 0x00000000, 0x00050048, 0x0000001f, 0x00000001, 0x0000000b,
|
||||
0x00000001, 0x00050048, 0x0000001f, 0x00000002, 0x0000000b, 0x00000003,
|
||||
0x00050048, 0x0000001f, 0x00000003, 0x0000000b, 0x00000004, 0x00030047,
|
||||
0x00000024, 0x00000002, 0x00040048, 0x00000024, 0x00000000, 0x00000005,
|
||||
0x00050048, 0x00000024, 0x00000000, 0x00000007, 0x00000010, 0x00050048,
|
||||
0x00000024, 0x00000000, 0x00000023, 0x00000000, 0x00040047, 0x0000002b,
|
||||
0x0000001e, 0x00000000, 0x00040047, 0x00000038, 0x0000001e, 0x00000000,
|
||||
0x00040047, 0x0000003a, 0x0000001e, 0x00000004, 0x00040047, 0x0000003d,
|
||||
0x0000001e, 0x00000001, 0x00040047, 0x0000003e, 0x0000001e, 0x00000005,
|
||||
0x00020013, 0x00000002, 0x00030021, 0x00000003, 0x00000002, 0x00030016,
|
||||
0x00000006, 0x00000020, 0x00040017, 0x00000007, 0x00000006, 0x00000002,
|
||||
0x00040020, 0x00000008, 0x00000007, 0x00000007, 0x00040015, 0x0000000a,
|
||||
0x00000020, 0x00000000, 0x0004002b, 0x0000000a, 0x0000000b, 0x00000006,
|
||||
0x0004001c, 0x0000000c, 0x00000007, 0x0000000b, 0x0004002b, 0x00000006,
|
||||
0x0000000d, 0x00000000, 0x0005002c, 0x00000007, 0x0000000e, 0x0000000d,
|
||||
0x0000000d, 0x0004002b, 0x00000006, 0x0000000f, 0x3f800000, 0x0005002c,
|
||||
0x00000007, 0x00000010, 0x0000000f, 0x0000000d, 0x0005002c, 0x00000007,
|
||||
0x00000011, 0x0000000f, 0x0000000f, 0x0005002c, 0x00000007, 0x00000012,
|
||||
0x0000000d, 0x0000000f, 0x0009002c, 0x0000000c, 0x00000013, 0x0000000e,
|
||||
0x00000010, 0x00000011, 0x00000011, 0x00000012, 0x0000000e, 0x00040015,
|
||||
0x00000014, 0x00000020, 0x00000001, 0x00040020, 0x00000015, 0x00000001,
|
||||
0x00000014, 0x0004003b, 0x00000015, 0x00000016, 0x00000001, 0x00040020,
|
||||
0x00000018, 0x00000007, 0x0000000c, 0x00040017, 0x0000001c, 0x00000006,
|
||||
0x00000004, 0x0004002b, 0x0000000a, 0x0000001d, 0x00000001, 0x0004001c,
|
||||
0x0000001e, 0x00000006, 0x0000001d, 0x0006001e, 0x0000001f, 0x0000001c,
|
||||
0x00000006, 0x0000001e, 0x0000001e, 0x00040020, 0x00000020, 0x00000003,
|
||||
0x0000001f, 0x0004003b, 0x00000020, 0x00000021, 0x00000003, 0x0004002b,
|
||||
0x00000014, 0x00000022, 0x00000000, 0x00040018, 0x00000023, 0x0000001c,
|
||||
0x00000004, 0x0003001e, 0x00000024, 0x00000023, 0x00040020, 0x00000025,
|
||||
0x00000009, 0x00000024, 0x0004003b, 0x00000025, 0x00000026, 0x00000009,
|
||||
0x00040020, 0x00000027, 0x00000009, 0x00000023, 0x00040020, 0x0000002a,
|
||||
0x00000001, 0x00000023, 0x0004003b, 0x0000002a, 0x0000002b, 0x00000001,
|
||||
0x00040020, 0x00000033, 0x00000003, 0x0000001c, 0x0004002b, 0x0000000a,
|
||||
0x00000035, 0x00000002, 0x00040020, 0x00000036, 0x00000003, 0x00000006,
|
||||
0x0004003b, 0x00000033, 0x00000038, 0x00000003, 0x00040020, 0x00000039,
|
||||
0x00000001, 0x0000001c, 0x0004003b, 0x00000039, 0x0000003a, 0x00000001,
|
||||
0x00040020, 0x0000003c, 0x00000003, 0x00000007, 0x0004003b, 0x0000003c,
|
||||
0x0000003d, 0x00000003, 0x0004003b, 0x00000039, 0x0000003e, 0x00000001,
|
||||
0x00050036, 0x00000002, 0x00000004, 0x00000000, 0x00000003, 0x000200f8,
|
||||
0x00000005, 0x0004003b, 0x00000008, 0x00000009, 0x00000007, 0x0004003b,
|
||||
0x00000018, 0x00000019, 0x00000007, 0x0004003d, 0x00000014, 0x00000017,
|
||||
0x00000016, 0x0003003e, 0x00000019, 0x00000013, 0x00050041, 0x00000008,
|
||||
0x0000001a, 0x00000019, 0x00000017, 0x0004003d, 0x00000007, 0x0000001b,
|
||||
0x0000001a, 0x0003003e, 0x00000009, 0x0000001b, 0x00050041, 0x00000027,
|
||||
0x00000028, 0x00000026, 0x00000022, 0x0004003d, 0x00000023, 0x00000029,
|
||||
0x00000028, 0x0004003d, 0x00000023, 0x0000002c, 0x0000002b, 0x00050092,
|
||||
0x00000023, 0x0000002d, 0x00000029, 0x0000002c, 0x0004003d, 0x00000007,
|
||||
0x0000002e, 0x00000009, 0x00050051, 0x00000006, 0x0000002f, 0x0000002e,
|
||||
0x00000000, 0x00050051, 0x00000006, 0x00000030, 0x0000002e, 0x00000001,
|
||||
0x00070050, 0x0000001c, 0x00000031, 0x0000002f, 0x00000030, 0x0000000d,
|
||||
0x0000000f, 0x00050091, 0x0000001c, 0x00000032, 0x0000002d, 0x00000031,
|
||||
0x00050041, 0x00000033, 0x00000034, 0x00000021, 0x00000022, 0x0003003e,
|
||||
0x00000034, 0x00000032, 0x00060041, 0x00000036, 0x00000037, 0x00000021,
|
||||
0x00000022, 0x00000035, 0x0003003e, 0x00000037, 0x0000000d, 0x0004003d,
|
||||
0x0000001c, 0x0000003b, 0x0000003a, 0x0003003e, 0x00000038, 0x0000003b,
|
||||
0x0004003d, 0x0000001c, 0x0000003f, 0x0000003e, 0x0007004f, 0x00000007,
|
||||
0x00000040, 0x0000003f, 0x0000003f, 0x00000000, 0x00000001, 0x0004003d,
|
||||
0x0000001c, 0x00000041, 0x0000003e, 0x0007004f, 0x00000007, 0x00000042,
|
||||
0x00000041, 0x00000041, 0x00000002, 0x00000003, 0x0004003d, 0x00000007,
|
||||
0x00000043, 0x00000009, 0x0008000c, 0x00000007, 0x00000044, 0x00000001,
|
||||
0x0000002e, 0x00000040, 0x00000042, 0x00000043, 0x0003003e, 0x0000003d,
|
||||
0x00000044, 0x000100fd, 0x00010038,
|
||||
};
|
||||
|
||||
} // namespace sophia::shaders
|
||||
Reference in New Issue
Block a user