#include "backend/rhi.h"
#include "core/logger.h"
#include "core/math_rtm.h"
#include "core/platform.h"
#include <chrono>
#include <cmath>
#include <cstring>
static constexpr u32 DH_TEX_W = 2;
static constexpr u32 DH_TEX_H = 2;
static constexpr u8 DH_TEX[2][DH_TEX_W * DH_TEX_H * 4] = {
{255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255},
{0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255},
};
static constexpr f32 DH_OFFSET[4] = {0.2f, -0.15f, 0.0f, 0.0f};
struct DhPush {
vec4 tint; // 0
u64 tex; // 16, slot (vk) / ResourceID (mtl)
u64 samp; // 24, slot (vk) / ResourceID (mtl)
u64 ubo; // 32, slot (vk) / device address (mtl)
u64 cbuf; // 40, slot (vk) / device address (mtl)
u64 bbuf; // 48, slot (vk) / device address (mtl)
u64 addr; // 56, device address (both)
};
static_assert(sizeof(DhPush) == 64, "DhPush size drift");
int main() {
vel::Logger::init();
rhi::Config config{};
config.vsync_enabled = true;
config.enable_validation = true;
rhi::Device device{};
rhi::Swapchain swapchain{};
rhi::CmdPool pool{};
rhi::CmdBuffer cmd{};
rhi::Sync *frame_sync = nullptr;
rhi::ShaderCompiler sc{};
rhi::RenderPipeline pipeline{};
Platform platform{};
platform.width = 800;
platform.height = 800;
init_platform(config.instance_extensions);
rhi::create_context(device, config);
rhi::create_memory_allocator(device);
rhi::init_compiler(sc);
create_window(device, platform, "descriptor-handle", WindowMode::Windowed, true);
rhi::SwapchainDesc swapchain_desc{};
swapchain_desc.native_window = platform_native_window(platform);
swapchain_desc.width = platform.width;
swapchain_desc.height = platform.height;
swapchain_desc.vsync_enabled = config.vsync_enabled;
swapchain_desc.image_count = config.swapchain_image_count;
rhi::create_swapchain(device, swapchain_desc, swapchain);
rhi::Queue graphics_queue = rhi::graphics_queue(device);
rhi::CmdPoolDesc cmd_pool_desc;
cmd_pool_desc.queue_family_index = rhi::queue_family_index(graphics_queue);
cmd_pool_desc.flags = rhi::CmdPoolUsage::Transient | rhi::CmdPoolUsage::ResetCommandBuffer;
rhi::create_cmd_pool(device, cmd_pool_desc, pool);
rhi::CmdBufferDesc cmd_desc{};
cmd_desc.pool = &pool;
cmd_desc.level = rhi::CmdBufferLevel::Primary;
rhi::create_cmd_buffer(device, cmd_desc, cmd);
frame_sync = rhi::sync_create(device, 0);
rhi::Buffer offset_buf{};
rhi::BufferView offset_view{};
rhi::BufferView offset_cview{};
rhi::BufferView offset_bview{};
{
rhi::BufferDesc d{};
d.size = sizeof(DH_OFFSET);
d.usage = rhi::BufferUsage::StorageBuffer | rhi::BufferUsage::UniformBuffer | rhi::BufferUsage::ShaderAddr;
d.memory = rhi::BufferMemType::Upload;
rhi::create_buffer(device, d, offset_buf);
std::memcpy(offset_buf.mapped, DH_OFFSET, sizeof(DH_OFFSET));
rhi::BufferViewDesc vd{};
vd.buffer = &offset_buf;
vd.offset = 0;
vd.size = sizeof(DH_OFFSET);
vd.type = rhi::BufferViewType::Storage;
if (!rhi::create_buffer_view(device, vd, offset_view)) {
VEL_CRITICAL("descriptor-handle: failed to create buffer view");
return 1;
}
vd.type = rhi::BufferViewType::Uniform;
if (!rhi::create_buffer_view(device, vd, offset_cview)) {
VEL_CRITICAL("descriptor-handle: failed to create constant buffer view");
return 1;
}
vd.type = rhi::BufferViewType::Storage;
if (!rhi::create_buffer_view(device, vd, offset_bview)) {
VEL_CRITICAL("descriptor-handle: failed to create byte buffer view");
return 1;
}
}
rhi::Sampler sampler{};
{
rhi::SamplerDesc sdesc{};
sdesc.filter = rhi::SamplerFilter::LINEAR;
sdesc.address = rhi::SamplerAddressMode::CLAMP;
rhi::create_sampler(device, sdesc, sampler);
}
rhi::Image tex_img[2];
rhi::ImageView tex_view[2];
rhi::Buffer staging{};
{
rhi::BufferDesc d{};
d.size = 2 * DH_TEX_W * DH_TEX_H * 4;
d.usage = rhi::BufferUsage::CopySrc;
d.memory = rhi::BufferMemType::Upload;
rhi::create_buffer(device, d, staging);
}
{
rhi::reset_cmd_buffer(cmd);
rhi::begin_cmd_buffer(cmd, rhi::CmdBufferBeginDesc{.usage = rhi::CmdBufferUsage::OneTimeSubmit});
for (int i = 0; i < 2; ++i) {
rhi::ImageDesc idesc{};
idesc.width = DH_TEX_W;
idesc.height = DH_TEX_H;
idesc.format = rhi::ImageFormat::RGBA8_UNORM;
idesc.usage = rhi::ImageUsage::Sampled | rhi::ImageUsage::TransferDst;
rhi::create_image(device, idesc, tex_img[i]);
rhi::ImageViewDesc vdesc{};
vdesc.image = &tex_img[i];
vdesc.aspect = rhi::ImageAspect::Color;
rhi::create_image_view(device, vdesc, tex_view[i]);
std::memcpy((u8 *)staging.mapped + i * DH_TEX_W * DH_TEX_H * 4, DH_TEX[i], DH_TEX_W * DH_TEX_H * 4);
rhi::ImageRange range{};
rhi::barrier(cmd, tex_img[i], range, rhi::ResourceState::Idle, rhi::ResourceState::TransferTo);
rhi::BufferTextureCopyRegion region{};
region.buffer_offset = (u64)i * DH_TEX_W * DH_TEX_H * 4;
region.layer_count = 1;
region.texture_extent_width = DH_TEX_W;
region.texture_extent_height = DH_TEX_H;
region.texture_extent_depth = 1;
rhi::copy_buffer_to_texture(cmd, staging, tex_view[i], region);
rhi::barrier(cmd, tex_img[i], range, rhi::ResourceState::TransferTo, rhi::ResourceState::TextureSample);
tex_img[i].state = rhi::ResourceState::TextureSample;
}
rhi::end_cmd_buffer(cmd);
rhi::QueueSubmitDesc submit{};
submit.cmd_count = 1;
submit.cmds = &cmd;
rhi::queue_submit(device, graphics_queue, submit);
rhi::queue_wait_idle(device);
}
rhi::RenderPipelineDesc pd{};
pd.device = &device;
pd.set_shaders(sc, "conformance/descriptor_handle.slang", "vsMain", "fsMain");
pd.set_depth_testing(false, false, rhi::PipelineCompareOp::LESS);
pd.set_cull_mode(rhi::PipelineCullMode::NONE, rhi::PipelineTriangleWindingOrder::CCW);
pd.set_color_format(rhi::swapchain_format(swapchain));
pipeline = pd.build();
if (!rhi::valid(pipeline)) {
VEL_CRITICAL("descriptor-handle: pipeline build failed");
return 1;
}
const auto start = std::chrono::steady_clock::now();
u64 frame_count = 0;
while (platform_update(platform)) {
if (platform.framebuffer_resized || rhi::swapchain_needs_recreate(swapchain)) {
platform.framebuffer_resized = false;
if (!platform_wait_for_valid_framebuffer(platform)) {
break;
}
rhi::queue_wait_idle(device);
rhi::destroy_swapchain(device, swapchain);
swapchain_desc.width = platform.width;
swapchain_desc.height = platform.height;
rhi::create_swapchain(device, swapchain_desc, swapchain);
continue;
}
const f32 time = std::chrono::duration<f32>(std::chrono::steady_clock::now() - start).count();
const u32 alt = (u32)((u64)time % 2);
DhPush push{};
const f32 pulse = 0.75f + 0.25f * std::sin(time * 2.0f);
push.tint = vec4(pulse, pulse, pulse, 1.0f);
push.tex = rhi::handle_id(device, tex_view[alt]);
push.samp = rhi::handle_id(device, sampler);
push.ubo = rhi::handle_id(device, offset_view);
push.cbuf = rhi::handle_id(device, offset_cview);
push.bbuf = rhi::handle_id(device, offset_bview);
push.addr = rhi::buffer_device_address(device, offset_buf);
if (tex_view[alt].slot == UINT64_MAX || sampler.slot == UINT64_MAX || offset_view.slot == UINT64_MAX ||
offset_cview.slot == UINT64_MAX || offset_bview.slot == UINT64_MAX || push.addr == 0) {
VEL_CRITICAL(
"descriptor-handle: null handle (tex={} samp={} ubo={} cbuf={} bbuf={} addr={})",
push.tex,
push.samp,
push.ubo,
push.cbuf,
push.bbuf,
push.addr
);
return 1;
}
rhi::reset_cmd_buffer(cmd);
rhi::begin_cmd_buffer(cmd, rhi::CmdBufferBeginDesc{.usage = rhi::CmdBufferUsage::OneTimeSubmit});
u32 image_index = 0;
if (!rhi::acquire_swapchain_image(device, swapchain, image_index, cmd)) {
rhi::reset_cmd_buffer(cmd);
continue;
}
rhi::Image *swap_image = rhi::swapchain_image(swapchain, image_index);
rhi::Extent2D extent = rhi::swapchain_extent(swapchain);
rhi::ImageView swap_view = rhi::get_cached_image_view(device, *swap_image, {.aspect = rhi::ImageAspect::Color});
rhi::AttachmentDesc color_att{};
color_att.img = &swap_view;
color_att.loadOp = rhi::LoadOp::CLEAR;
color_att.storeOp = rhi::StoreOp::STORE;
color_att.clearValue.color.float32[0] = 0.1f;
color_att.clearValue.color.float32[1] = 0.1f;
color_att.clearValue.color.float32[2] = 0.1f;
color_att.clearValue.color.float32[3] = 1.0f;
rhi::RenderingInfo ri{};
ri.colorAttachments[0] = color_att;
ri.colorAttachmentCount = 1;
ri.width = extent.width;
ri.height = extent.height;
rhi::begin_rendering(cmd, ri);
rhi::set_pipeline(cmd, pipeline);
rhi::Viewport vp{};
vp.width = (f32)extent.width;
vp.height = (f32)extent.height;
rhi::set_viewports(cmd, &vp);
rhi::Rect scissor{};
scissor.width = (i16)extent.width;
scissor.height = (i16)extent.height;
rhi::set_scissors(cmd, &scissor);
rhi::set_constants(cmd, pipeline, rhi::ShaderStage::VERTEX | rhi::ShaderStage::FRAGMENT, sizeof(push), &push);
rhi::draw(cmd, 3, 1, 0, 0);
rhi::end_rendering(cmd);
rhi::ImageRange range{};
rhi::barrier(cmd, *swap_image, range, swap_image->state, rhi::ResourceState::Display);
rhi::end_cmd_buffer(cmd);
rhi::QueueSignal completion{{frame_sync, frame_count + 1}};
rhi::QueueSubmitDesc submit{};
submit.cmd_count = 1;
submit.cmds = &cmd;
submit.signal_count = 1;
submit.signals = &completion;
rhi::queue_submit(device, graphics_queue, submit);
rhi::present_swapchain(device, swapchain, image_index, {frame_sync, frame_count + 1});
++frame_count;
rhi::sync_host_wait(frame_sync, frame_count);
}
rhi::device_wait_idle(device);
rhi::destroy_pipeline(device, pipeline);
rhi::destroy_buffer(device, staging);
for (int i = 0; i < 2; ++i) {
rhi::destroy_image_view(device, tex_view[i]);
rhi::destroy_image(device, tex_img[i]);
}
rhi::destroy_sampler(device, sampler);
rhi::destroy_buffer_view(device, offset_bview);
rhi::destroy_buffer_view(device, offset_cview);
rhi::destroy_buffer_view(device, offset_view);
rhi::destroy_buffer(device, offset_buf);
rhi::sync_destroy(frame_sync);
rhi::destroy_cmd_buffer(device, pool, cmd);
rhi::destroy_cmd_pool(device, pool);
rhi::destroy_swapchain(device, swapchain);
rhi::destroy_memory_allocator(device);
destroy_window(device, platform);
rhi::destroy_context(device);
vel::Logger::shutdown();
return 0;
}