diff --git a/aster/include/aster/systems/commit_manager.h b/aster/include/aster/systems/commit_manager.h index 1645e20..3f922d6 100644 --- a/aster/include/aster/systems/commit_manager.h +++ b/aster/include/aster/systems/commit_manager.h @@ -20,6 +20,7 @@ namespace systems { +class Device; class CommitManager { @@ -286,6 +287,11 @@ class CommitManager return *m_Instance; } + static bool IsInit() + { + return static_cast(m_Instance); + } + private: vk::DescriptorPool m_DescriptorPool; vk::DescriptorSetLayout m_SetLayout; diff --git a/aster/include/aster/systems/device.h b/aster/include/aster/systems/device.h index 874af27..fbf1ddd 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -19,9 +19,9 @@ #include "aster/core/size.h" #include "aster/core/swapchain.h" +#include "EASTL/deque.h" #include #include -#include "EASTL/deque.h" #include #include @@ -371,6 +371,7 @@ struct DeviceCreateInfo PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector; std::span m_PipelineCacheData = {}; eastl::vector m_ShaderSearchPaths; + bool m_UseBindless = true; cstr m_Name = "Primary"; }; @@ -401,7 +402,7 @@ struct Frame; class Context { - protected: +protected: vk::CommandBuffer m_Cmd; friend Device; @@ -505,6 +506,14 @@ struct Frame GraphicsContext CreateGraphicsContext(); TransferContext CreateTransferContext(); void WaitUntilReady(); + + Frame() = default; + Frame(Device &device, u32 primaryQueueFamily, u32 frameIndex); + Frame(Frame &&other) noexcept; + + Frame &operator=(Frame &&other) noexcept; + + DISALLOW_COPY_AND_ASSIGN(Frame); }; class Device final @@ -515,6 +524,7 @@ class Device final Surface m_Surface; ::Device m_Device; Swapchain m_Swapchain; + std::unique_ptr m_CommitManager; // TODO: This is single-threaded. vk::Queue m_GraphicsQueue; diff --git a/aster/src/aster/systems/commit_manager.cpp b/aster/src/aster/systems/commit_manager.cpp index 0141214..434195e 100644 --- a/aster/src/aster/systems/commit_manager.cpp +++ b/aster/src/aster/systems/commit_manager.cpp @@ -8,6 +8,7 @@ #include "EASTL/array.h" #include "core/device.h" #include "core/image_view.h" +#include "systems/device.h" using namespace systems; @@ -44,7 +45,7 @@ CommitManager::CommitManager(const Device *device, const u32 maxBuffers, const u .poolSizeCount = static_cast(poolSizes.size()), .pPoolSizes = poolSizes.data(), }; - AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool)); + AbortIfFailed(device->m_Device->createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool)); eastl::array descriptorLayoutBindings = { vk::DescriptorSetLayoutBinding{ @@ -85,7 +86,7 @@ CommitManager::CommitManager(const Device *device, const u32 maxBuffers, const u .bindingCount = static_cast(descriptorLayoutBindings.size()), .pBindings = descriptorLayoutBindings.data(), }; - AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout)); + AbortIfFailed(device->m_Device->createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout)); // One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending) // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html @@ -95,7 +96,7 @@ CommitManager::CommitManager(const Device *device, const u32 maxBuffers, const u .descriptorSetCount = 1, .pSetLayouts = &m_SetLayout, }; - AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet)); + AbortIfFailed(device->m_Device->allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet)); device->SetName(m_SetLayout, "Bindless Layout"); device->SetName(m_DescriptorPool, "Bindless Pool"); @@ -106,8 +107,8 @@ CommitManager::CommitManager(const Device *device, const u32 maxBuffers, const u CommitManager::~CommitManager() { - m_Device->m_Device.destroy(m_SetLayout, nullptr); - m_Device->m_Device.destroy(m_DescriptorPool, nullptr); + m_Device->m_Device->destroy(m_SetLayout, nullptr); + m_Device->m_Device->destroy(m_DescriptorPool, nullptr); #if !defined(ASTER_NDEBUG) u32 bufferCount = 0; @@ -227,7 +228,7 @@ CommitManager::Update() // Descriptor Updates if (!m_Writes.empty()) { - m_Device->m_Device.updateDescriptorSets(static_cast(m_Writes.size()), m_Writes.data(), 0, nullptr); + m_Device->m_Device->updateDescriptorSets(static_cast(m_Writes.size()), m_Writes.data(), 0, nullptr); m_Writes.clear(); m_WriteInfos.clear(); diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 78cde23..182d66a 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -11,6 +11,7 @@ #include "aster/systems/sync_server.h" #include "aster/util/files.h" +#include "systems/commit_manager.h" #include #include @@ -615,8 +616,9 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre .sampleShadingEnable = false, }; vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { - .depthTestEnable = false, - .depthWriteEnable = false, + .depthTestEnable = true, + .depthWriteEnable = true, + .depthCompareOp = vk::CompareOp::eLess, }; vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { .blendEnable = false, @@ -919,11 +921,29 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, // TODO: Reflect to create the push constants and descriptor sets. + // TODO: Hackery. To FIX. + vk::PushConstantRange pcr{ + .stageFlags = vk::ShaderStageFlagBits::eAllGraphics, + .offset = 0, + .size = 24, + }; + + u32 setLayoutCount = 0; + const vk::DescriptorSetLayout *setLayout = nullptr; + + if (m_CommitManager) + { + setLayoutCount = 1; + setLayout = &m_CommitManager->GetDescriptorSetLayout(); + } + + // TODO: END EXPERIMENT + const vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { - .setLayoutCount = 0, - .pSetLayouts = nullptr, - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, + .setLayoutCount = setLayoutCount, + .pSetLayouts = setLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pcr, }; vk::Result result = m_Device->createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); if (Failed(result)) @@ -1043,6 +1063,11 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()}; + if (createInfo.m_UseBindless) + { + m_CommitManager = std::make_unique(this, 1000, 1000, 1000, CreateSampler({})); + } + const vk::CommandPoolCreateInfo transferPoolCreateInfo = { .flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer, .queueFamilyIndex = m_TransferQueueFamily, @@ -1057,11 +1082,24 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) .name = slang::CompilerOptionName::VulkanUseEntryPointName, .value = slang::CompilerOptionValue{.kind = slang::CompilerOptionValueKind::Int, .intValue0 = 1}, }; + slang::CompilerOptionEntry bindlessSpaceIndex = { + .name = slang::CompilerOptionName::BindlessSpaceIndex, + .value = slang::CompilerOptionValue{.kind = slang::CompilerOptionValueKind::Int, .intValue0 = 100}, + }; + slang::CompilerOptionEntry scalarLayout = { + .name = slang::CompilerOptionName::GLSLForceScalarLayout, + .value = slang::CompilerOptionValue{.kind = slang::CompilerOptionValueKind::Int, .intValue0 = 1}, + }; + std::array compilerOptions = { + useOriginalEntrypointNames, + bindlessSpaceIndex, + }; + const slang::TargetDesc spirvTargetDesc = { .format = SLANG_SPIRV, .profile = m_GlobalSlangSession->findProfile("glsl_450"), - .compilerOptionEntries = &useOriginalEntrypointNames, - .compilerOptionEntryCount = 1, + .compilerOptionEntries = compilerOptions.data(), + .compilerOptionEntryCount = static_cast(compilerOptions.size()), }; const slang::SessionDesc sessionDesc = { .targets = &spirvTargetDesc, @@ -1075,7 +1113,7 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) u32 index = 0; for (auto &frame : m_Frames) { - frame = CreateFrame(index++); + frame = Frame(*this, m_PrimaryQueueFamily, index++); } } @@ -1097,51 +1135,6 @@ systems::Device::~Device() #pragma region Frames // ==================================================================================================== -systems::Frame -systems::Device::CreateFrame(u32 frameIndex) -{ - vk::CommandPool pool; - vk::Fence fence; - vk::Semaphore acquire; - vk::Semaphore finished; - - NameString name = "Frame "; - name += static_cast(frameIndex + '0'); - - const vk::CommandPoolCreateInfo commandPoolCreateInfo = { - .flags = vk::CommandPoolCreateFlagBits::eTransient, - .queueFamilyIndex = m_PrimaryQueueFamily, - }; - AbortIfFailedMV(m_Device->createCommandPool(&commandPoolCreateInfo, nullptr, &pool), - "Could not command pool for frame {}", frameIndex); - - constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled}; - AbortIfFailedMV(m_Device->createFence(&fenceCreateInfo, nullptr, &fence), - "Could not create a fence for frame {}", frameIndex); - - constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {}; - AbortIfFailedMV(m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &acquire), - "Could not create IA semaphore for frame {}.", frameIndex); - AbortIfFailedMV(m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &finished), - "Could not create RF semaphore for frame {}.", frameIndex); - - m_Device.SetName(pool, name.c_str()); - m_Device.SetName(fence, name.c_str()); - m_Device.SetName(acquire, name.c_str()); - m_Device.SetName(finished, name.c_str()); - - DEBUG("Frame {} created successfully.", frameIndex); - - return Frame{ - .m_Device = this, - .m_Pool = pool, - .m_FrameAvailableFence = fence, - .m_ImageAcquireSem = acquire, - .m_RenderFinishSem = finished, - .m_FrameIdx = frameIndex, - }; -} - systems::Frame & systems::Device::GetNextFrame() { @@ -1156,8 +1149,8 @@ systems::Device::GetNextFrame() bool imageAcquired = false; while (!imageAcquired) { - switch (auto result = m_Device->acquireNextImageKHR( - m_Swapchain.m_Swapchain, MaxValue, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex)) + switch (auto result = m_Device->acquireNextImageKHR(m_Swapchain.m_Swapchain, MaxValue, + currentFrame.m_ImageAcquireSem, nullptr, &imageIndex)) { case vk::Result::eSuccess: case vk::Result::eSuboptimalKHR: // Suboptimal can still render. Better to let this go for semaphores etc. @@ -1350,6 +1343,75 @@ systems::Frame::WaitUntilReady() "Waiting for fence {} failed.", m_FrameIdx); } +systems::Frame::Frame(Device &device, u32 primaryQueueFamily, u32 frameIndex) + : m_Device{&device} + , m_FrameIdx{frameIndex} + , m_ImageIdx{0} +{ + NameString name = "Frame "; + name += static_cast(frameIndex + '0'); + + const vk::CommandPoolCreateInfo commandPoolCreateInfo = { + .flags = vk::CommandPoolCreateFlagBits::eTransient, + .queueFamilyIndex = primaryQueueFamily, + }; + AbortIfFailedMV(device.m_Device->createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool), + "Could not command pool for frame {}", frameIndex); + + constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled}; + AbortIfFailedMV(device.m_Device->createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence), + "Could not create a fence for frame {}", frameIndex); + + constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {}; + AbortIfFailedMV(device.m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem), + "Could not create IA semaphore for frame {}.", frameIndex); + AbortIfFailedMV(device.m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem), + "Could not create RF semaphore for frame {}.", frameIndex); + + m_Device->SetName(m_Pool, name.c_str()); + m_Device->SetName(m_FrameAvailableFence, name.c_str()); + m_Device->SetName(m_ImageAcquireSem, name.c_str()); + m_Device->SetName(m_RenderFinishSem, name.c_str()); + + DEBUG("Frame {} created successfully.", frameIndex); +} + +systems::Frame & +systems::Frame::operator=(Frame &&other) noexcept +{ + if (this == &other) + return *this; + m_Device = other.m_Device; + m_Pool = Take(other.m_Pool); + m_FrameAvailableFence = Take(other.m_FrameAvailableFence); + m_ImageAcquireSem = Take(other.m_ImageAcquireSem); + m_RenderFinishSem = Take(other.m_RenderFinishSem); + m_FrameIdx = other.m_FrameIdx; + m_CommandBuffers = Take(other.m_CommandBuffers); + m_SwapchainImage = Take(other.m_SwapchainImage); + m_SwapchainImageView = Take(other.m_SwapchainImageView); + m_SwapchainSize = Take(other.m_SwapchainSize); + m_ImageIdx = other.m_ImageIdx; + m_CommandBuffersAllocated = other.m_CommandBuffersAllocated; + return *this; +} + +systems::Frame::Frame(Frame &&other) noexcept + : m_Device{Take(other.m_Device)} + , m_Pool{Take(other.m_Pool)} + , m_FrameAvailableFence{Take(other.m_FrameAvailableFence)} + , m_ImageAcquireSem{Take(other.m_ImageAcquireSem)} + , m_RenderFinishSem{Take(other.m_RenderFinishSem)} + , m_FrameIdx{other.m_FrameIdx} + , m_CommandBuffers{Take(other.m_CommandBuffers)} + , m_SwapchainImage{Take(other.m_SwapchainImage)} + , m_SwapchainImageView{Take(other.m_SwapchainImageView)} + , m_SwapchainSize{Take(other.m_SwapchainSize)} + , m_ImageIdx{other.m_ImageIdx} + , m_CommandBuffersAllocated{other.m_CommandBuffersAllocated} +{ +} + #pragma endregion // ==================================================================================================== @@ -1397,6 +1459,14 @@ void systems::GraphicsContext::BindPipeline(const Pipeline &pipeline) { m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + + // TODO: Maybe find a smarter place to host this. + if (CommitManager::IsInit()) + { + m_Cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, + &CommitManager::Instance().GetDescriptorSet(), 0, nullptr); + } + m_PipelineInUse = &pipeline; } diff --git a/samples/01_triangle/triangle.cpp b/samples/01_triangle/triangle.cpp index 07dd4fb..bcc1522 100644 --- a/samples/01_triangle/triangle.cpp +++ b/samples/01_triangle/triangle.cpp @@ -57,21 +57,21 @@ main(int, char **) .m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}}, .m_AppName = "Triangle", .m_ShaderSearchPaths = {"shader/"}, + .m_UseBindless = false, .m_Name = "Primary", }}; Pipeline pipeline; - auto pipelineError = device.CreatePipeline( - pipeline, { - .m_VertexInputs = {{ - .m_Attribute = Vertex::GetAttributes(), - .m_Stride = sizeof(Vertex), - }}, - .m_Shaders = {{ - .m_ShaderFile = SHADER_MODULE, - .m_EntryPoints = {"vsmain", "fsmain"}, - }}, - }); + auto pipelineError = device.CreatePipeline(pipeline, { + .m_VertexInputs = {{ + .m_Attribute = Vertex::GetAttributes(), + .m_Stride = sizeof(Vertex), + }}, + .m_Shaders = {{ + .m_ShaderFile = SHADER_MODULE, + .m_EntryPoints = {"vsmain", "fsmain"}, + }}, + }); ERROR_IF(pipelineError, "Error creating pipeline. Cause: {}", pipelineError.What()); // eastl::array vertices{}; @@ -83,7 +83,6 @@ main(int, char **) auto vbo = device.CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO"); vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data()); - // Persistent variables vk::ImageSubresourceRange subresourceRange = { diff --git a/samples/02_box/CMakeLists.txt b/samples/02_box/CMakeLists.txt index 982df96..a554695 100644 --- a/samples/02_box/CMakeLists.txt +++ b/samples/02_box/CMakeLists.txt @@ -9,6 +9,7 @@ add_shader(box "shader/box.vert.glsl") add_shader(box "shader/box.frag.glsl") add_shader(box "shader/box.vs.hlsl") add_shader(box "shader/box.ps.hlsl") +add_shader(box "shader/triangle.slang") add_resource_dir(box "shader/") target_link_libraries(box PRIVATE aster_core) diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 353ec5d..b01b557 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -119,9 +119,6 @@ main(int, char **) .m_ShaderSearchPaths = {"shader/"}, }}; - // TODO: Device in CommitManager. - systems::CommitManager commitManager{&device.m_Device, 12, 12, 12, device.CreateSampler({})}; - Pipeline pipeline; auto pipelineResult = device.CreatePipeline(pipeline, {.m_Shaders = { @@ -325,16 +322,23 @@ main(int, char **) initDepthImages(extent); }; + // TODO: Move to 64bit handles. struct PCB { - systems::ResId m_Camera; systems::ResId m_VertexBuffer; + u32 m_Pad0 = 0; + systems::ResId m_Camera; + u32 m_Pad1 = 0; systems::ResId m_Texture; + u32 m_Pad2 = 0; }; + static_assert(sizeof(PCB) == 24); + + auto &commitManager = systems::CommitManager::Instance(); PCB pcb = { - .m_Camera = commitManager.CommitBuffer(ubo), .m_VertexBuffer = commitManager.CommitBuffer(vbo), + .m_Camera = commitManager.CommitBuffer(ubo), .m_Texture = commitManager.CommitTexture(crate), }; @@ -351,7 +355,7 @@ main(int, char **) camera.m_Model *= rotate(mat4{1.0f}, static_cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo->Write(0, sizeof camera, &camera); - auto currentFrame = device.GetNextFrame(); + auto& currentFrame = device.GetNextFrame(); prevSwapchainSize = swapchainSize; swapchainSize = currentFrame.m_SwapchainSize; @@ -420,10 +424,8 @@ main(int, char **) context.SetViewport(viewport); context.BindPipeline(pipeline); - /*cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, - &commitManager.GetDescriptorSet(), 0, nullptr);*/ - //context.PushConstantBlock(pcb); - context.Draw(3); + context.PushConstantBlock(pcb); + context.Draw(vertices.size()); context.EndRendering(); diff --git a/samples/02_box/shader/bindless.slang b/samples/02_box/shader/bindless.slang new file mode 100644 index 0000000..86463eb --- /dev/null +++ b/samples/02_box/shader/bindless.slang @@ -0,0 +1,23 @@ +[vk::binding(0, 0)] __DynamicResource<__DynamicResourceKind.General> gBuffers[]; +[vk::binding(1, 0)] __DynamicResource<__DynamicResourceKind.Sampler> gSamplers[]; +[vk::binding(2, 0)] __DynamicResource<__DynamicResourceKind.General> gStorageTextures[]; + +export T getDescriptorFromHandle(DescriptorHandle handle) where T : IOpaqueDescriptor +{ + __target_switch + { + case spirv: + switch (T.kind) { + case DescriptorKind.Buffer: + return gBuffers[((uint2)handle).x].asOpaqueDescriptor(); + case DescriptorKind.CombinedTextureSampler: + return gSamplers[((uint2)handle).x].asOpaqueDescriptor(); + case DescriptorKind.Texture: + return gStorageTextures[((uint2)handle).x].asOpaqueDescriptor(); + default: + return defaultGetDescriptorFromHandle(handle); + } + default: + return defaultGetDescriptorFromHandle(handle); + } +} \ No newline at end of file diff --git a/samples/02_box/shader/triangle.slang b/samples/02_box/shader/triangle.slang index e178cee..cbad9e8 100644 --- a/samples/02_box/shader/triangle.slang +++ b/samples/02_box/shader/triangle.slang @@ -1,46 +1,59 @@ +import bindless; -struct Vertex { - float3 point; - float3 color; +struct VertexData +{ + float4 position; + float2 texCoord0; + float2 _pad0; }; +struct CameraData +{ + float4x4 model; + float4x4 view; + float4x4 projection; +}; + +struct PCB { + DescriptorHandle> vertexBuffer; + DescriptorHandle> cameraBuffer; + DescriptorHandle texture; +}; + +//uniform CameraData camera; +[vk::push_constant] +uniform PCB pcb; + struct VSIn { - uint VertexIndex : SV_VertexID; + uint vertexIndex : SV_VertexID; }; struct VSOut { - float4 Pos : SV_POSITION; - float3 Color : COLOR0; + float4 position : SV_POSITION; + float2 texCoord0 : TEXCOORD0; }; -static const float3 points[] = { - float3(-0.5f, -0.5f, 0.5f), - float3(0.5f, -0.5f, 0.5f), - float3(0.0f, 0.5f, 0.5f), -}; -static const float3 colors[] = { - float3(1.0f, 0.0f, 0.0f), - float3(0.0f, 1.0f, 0.0f), - float3(0.0f, 0.0f, 1.0f), -}; - -[shader("vertex")] -VSOut vsmain(VSIn input) { - VSOut output; - output.Pos = float4(points[input.VertexIndex], 1.0f); - output.Color = colors[input.VertexIndex]; - - return output; -} - struct FSOut { float4 Color; }; +[shader("vertex")] +func vsmain(VSIn input) -> VSOut { + VSOut output; + + VertexData vd = pcb.vertexBuffer[input.vertexIndex]; + + output.position = mul(mul(mul(float4(vd.position.xyz, 1.0f), pcb.cameraBuffer[0].model), pcb.cameraBuffer[0].view), pcb.cameraBuffer[0].projection); + output.texCoord0 = vd.texCoord0; + + return output; +} + [shader("fragment")] -FSOut fsmain(VSOut input) { +func fsmain(VSOut input) -> FSOut { FSOut outp; - outp.Color = float4(input.Color, 1.0); + outp.Color = pcb.vertexBuffer[0].position.rgba; + outp.Color = float4(pcb.texture.Sample(input.texCoord0).rgb, 1.0); return outp; } \ No newline at end of file