Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit ab649fd

Browse files
authored
Add Graphics::Texture, demo 2x2 RGBM texture (#26)
* Add `Graphics::Texture` * Add fallback texture in resource pool * Add `TextureSampler` and its pooling machinery * Add descriptor set binding for (main) texture * Add textures to `Primitive` and `BasicDrawable` * Sample texture in shaders * Demonstrate RGBM texture * RGBM ordering
1 parent 034fbf0 commit ab649fd

17 files changed

+179
-8
lines changed

App/Src/Main.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <Tkge/Graphics/Shader.hpp>
55
#include <Tkge/Utilities.hpp>
66
#include <klib/assert.hpp>
7+
#include <kvf/color_bitmap.hpp>
78
#include <kvf/time.hpp>
89
#include <cmath>
910
#include <exception>
@@ -57,7 +58,16 @@ namespace
5758
auto quad = Tkge::Graphics::Quad{};
5859
quad.Create(glm::vec2{400.0f});
5960
quad.transform.position.x = -250.0f;
60-
quad.tint = kvf::magenta_v;
61+
62+
auto colourBitmap = kvf::ColorBitmap{glm::ivec2{2, 2}};
63+
colourBitmap[0, 0] = kvf::red_v;
64+
colourBitmap[1, 0] = kvf::green_v;
65+
colourBitmap[0, 1] = kvf::blue_v;
66+
colourBitmap[1, 1] = kvf::magenta_v;
67+
auto texture = Tkge::Graphics::Texture{&engine.RenderDevice()};
68+
texture.Create(colourBitmap.bitmap());
69+
texture.sampler.filter = vk::Filter::eNearest;
70+
quad.texture = &texture;
6171

6272
auto instancedQuad = Tkge::Graphics::InstancedQuad{};
6373
instancedQuad.Create(glm::vec2{150.0f});

Assets/Shaders/Default.frag

280 Bytes
Binary file not shown.

Assets/Shaders/Default.vert

96 Bytes
Binary file not shown.

Lib/Include/Tkge/Engine.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace Tkge
2525
explicit Engine(const WindowSurface& surface = {}, vk::SampleCountFlagBits aa = AntiAliasing);
2626

2727
[[nodiscard]] const kvf::RenderDevice& RenderDevice() const { return _renderDevice; }
28+
[[nodiscard]] kvf::RenderDevice& RenderDevice() { return _renderDevice; }
2829

2930
[[nodiscard]] glm::ivec2 FramebufferSize() const;
3031
[[nodiscard]] auto FramebufferFormat() const -> vk::Format { return _renderPass.get_color_format(); }

Lib/Include/Tkge/Graphics/Drawable.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ namespace Tkge::Graphics
2424
.vertices = this->GetVertices(),
2525
.indices = this->GetIndices(),
2626
.topology = this->GetTopology(),
27+
.texture = texture,
2728
};
2829
}
30+
31+
const Texture* texture{nullptr};
2932
};
3033

3134
template <std::derived_from<IGeometry> TGeometry>

Lib/Include/Tkge/Graphics/Primitive.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include <Tkge/Graphics/Texture.hpp>
23
#include <Tkge/Graphics/Vertex.hpp>
34
#include <span>
45

@@ -10,5 +11,6 @@ namespace Tkge::Graphics
1011
std::span<const std::uint32_t> indices{};
1112

1213
vk::PrimitiveTopology topology{vk::PrimitiveTopology::eTriangleList};
14+
const Texture* texture{nullptr};
1315
};
1416
} // namespace Tkge::Graphics

Lib/Include/Tkge/Graphics/Renderer.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace Tkge::Graphics
4141
};
4242

4343
void UpdateInstances(std::span<const RenderInstance> instances);
44-
[[nodiscard]] bool WriteSets() const;
44+
[[nodiscard]] bool WriteSets(const Texture* texture) const;
4545
void BindVboAndDraw(const Primitive& primitive, std::uint32_t instances) const;
4646

4747
kvf::RenderPass* _renderPass{};

Lib/Include/Tkge/Graphics/ResourcePool.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include <Tkge/Graphics/PipelineFixedState.hpp>
33
#include <Tkge/Graphics/Shader.hpp>
4+
#include <Tkge/Graphics/Texture.hpp>
45
#include <kvf/vma.hpp>
56

67
namespace Tkge::Graphics
@@ -21,5 +22,8 @@ namespace Tkge::Graphics
2122

2223
/// \brief Allocate a Buffer for given usage and of given size.
2324
[[nodiscard]] virtual Buffer& AllocateBuffer(vk::BufferUsageFlags usage, vk::DeviceSize size) = 0;
25+
26+
[[nodiscard]] virtual vk::Sampler GetSampler(const Graphics::TextureSampler& sampler) = 0;
27+
[[nodiscard]] virtual const Graphics::Texture& GetFallbackTexture() const = 0;
2428
};
2529
} // namespace Tkge::Graphics

Lib/Include/Tkge/Graphics/Texture.hpp

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
#include <Tkge/Graphics/TextureSampler.hpp>
3+
#include <kvf/bitmap.hpp>
4+
#include <kvf/render_device.hpp>
5+
#include <kvf/vma.hpp>
6+
#include <cstddef>
7+
#include <span>
8+
9+
namespace Tkge::Graphics
10+
{
11+
class Texture
12+
{
13+
public:
14+
using Sampler = TextureSampler;
15+
16+
explicit Texture(gsl::not_null<kvf::RenderDevice*> renderDevice);
17+
18+
bool Create(const kvf::Bitmap& bitmap);
19+
bool Decompress(std::span<const std::byte> compressedImage);
20+
21+
[[nodiscard]] const kvf::vma::Image& GetImage() const { return _image; }
22+
[[nodiscard]] glm::ivec2 GetSize() const;
23+
24+
Sampler sampler{};
25+
26+
private:
27+
kvf::vma::Image _image{};
28+
};
29+
} // namespace Tkge::Graphics
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
#include <vulkan/vulkan.hpp>
3+
4+
namespace Tkge::Graphics
5+
{
6+
struct TextureSampler
7+
{
8+
vk::SamplerAddressMode wrap{vk::SamplerAddressMode::eRepeat};
9+
vk::Filter filter{vk::Filter::eLinear};
10+
vk::SamplerMipmapMode mipMap{vk::SamplerMipmapMode::eNearest};
11+
vk::BorderColor borderColour{vk::BorderColor::eFloatTransparentBlack};
12+
13+
bool operator==(const TextureSampler&) const = default;
14+
};
15+
} // namespace Tkge::Graphics

Lib/Src/Detail/PipelinePool.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,13 @@ namespace Tkge::Detail
7575
{
7676
static constexpr auto StageFlags = vk::ShaderStageFlagBits::eAllGraphics;
7777
// set 0: builtin
78-
auto set0 = std::array<vk::DescriptorSetLayoutBinding, 2>{};
78+
auto set0 = std::array<vk::DescriptorSetLayoutBinding, 3>{};
7979
// set 0, binding 0: view
8080
set0[0].setBinding(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eUniformBuffer).setStageFlags(StageFlags);
8181
// set 0, binding 1: instances
8282
set0[1].setBinding(1).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eStorageBuffer).setStageFlags(StageFlags);
83-
// TODO: texture bindings
83+
// set0, binding 2: texture
84+
set0[2].setBinding(2).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eCombinedImageSampler).setStageFlags(StageFlags);
8485

8586
// TODO: set 1: user data
8687

Lib/Src/Detail/SamplerPool.hpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
#include <Tkge/Graphics/TextureSampler.hpp>
3+
#include <klib/hash_combine.hpp>
4+
#include <kvf/render_device.hpp>
5+
#include <vulkan/vulkan_hash.hpp>
6+
#include <unordered_map>
7+
8+
namespace Tkge::Detail
9+
{
10+
class SamplerPool
11+
{
12+
public:
13+
explicit SamplerPool(gsl::not_null<const kvf::RenderDevice*> renderDevice) : _renderDevice(renderDevice) {}
14+
15+
[[nodiscard]] vk::Sampler GetSampler(const Graphics::TextureSampler& in)
16+
{
17+
const auto key = GetHash(in);
18+
auto it = _samplers.find(key);
19+
if (it != _samplers.end()) { return *it->second; }
20+
21+
auto sci = _renderDevice->sampler_info(in.wrap, in.filter);
22+
sci.setMipmapMode(in.mipMap).setBorderColor(in.borderColour);
23+
it = _samplers.insert({key, _renderDevice->get_device().createSamplerUnique(sci)}).first;
24+
return *it->second;
25+
}
26+
27+
private:
28+
[[nodiscard]] static std::size_t GetHash(const Graphics::TextureSampler& sampler)
29+
{
30+
return klib::make_combined_hash(sampler.wrap, sampler.filter, sampler.mipMap, sampler.borderColour);
31+
}
32+
33+
gsl::not_null<const kvf::RenderDevice*> _renderDevice;
34+
35+
std::unordered_map<std::size_t, vk::UniqueSampler> _samplers{};
36+
};
37+
} // namespace Tkge::Detail

Lib/Src/Engine.cpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <Detail/BufferPool.hpp>
22
#include <Detail/PipelinePool.hpp>
3+
#include <Detail/SamplerPool.hpp>
34
#include <Tkge/Engine.hpp>
45
#include <klib/assert.hpp>
56
#include <kvf/is_positive.hpp>
@@ -10,12 +11,22 @@ namespace Tkge
1011
{
1112
namespace
1213
{
14+
struct PixelBitmap
15+
{
16+
[[nodiscard]] constexpr kvf::Bitmap ToBitmap() const { return kvf::Bitmap{.bytes = bytes, .size = {1, 1}}; }
17+
18+
std::array<std::byte, 4> bytes{};
19+
};
20+
21+
constexpr auto WhiteBitmap = std::bit_cast<PixelBitmap>(kvf::white_v);
22+
1323
class ResourcePool : public Graphics::IResourcePool
1424
{
1525
public:
1626
explicit ResourcePool(gsl::not_null<kvf::RenderDevice*> renderDevice, vk::SampleCountFlagBits framebufferSamples)
17-
: _pipelinePool(renderDevice, framebufferSamples), _bufferPool(renderDevice)
27+
: _pipelinePool(renderDevice, framebufferSamples), _bufferPool(renderDevice), _samplerPool(renderDevice), _whiteTexture(renderDevice)
1828
{
29+
_whiteTexture.Create(WhiteBitmap.ToBitmap());
1930
}
2031

2132
[[nodiscard]] vk::PipelineLayout PipelineLayout() const final { return _pipelinePool.PipelineLayout(); }
@@ -32,11 +43,16 @@ namespace Tkge
3243
return _bufferPool.Allocate(usage, size);
3344
}
3445

46+
[[nodiscard]] vk::Sampler GetSampler(const Graphics::TextureSampler& sampler) final { return _samplerPool.GetSampler(sampler); }
47+
[[nodiscard]] const Graphics::Texture& GetFallbackTexture() const final { return _whiteTexture; }
48+
3549
void NextFrame() { _bufferPool.NextFrame(); }
3650

3751
private:
3852
Detail::PipelinePool _pipelinePool;
3953
Detail::BufferPool _bufferPool;
54+
Detail::SamplerPool _samplerPool;
55+
Graphics::Texture _whiteTexture;
4056
};
4157
} // namespace
4258

Lib/Src/Glsl/Default.frag

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#version 450 core
22

3+
layout (set = 0, binding = 2) uniform sampler2D tex;
4+
35
layout (location = 0) in vec4 inColour;
6+
layout (location = 1) in vec2 inUv;
47

58
layout (location = 0) out vec4 outColour;
69

710
void main()
811
{
9-
outColour = inColour;
12+
outColour = inColour * texture(tex, inUv);
1013
}

Lib/Src/Glsl/Default.vert

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ layout (set = 0, binding = 1) readonly buffer Instances
2121
};
2222

2323
layout (location = 0) out vec4 outColour;
24+
layout (location = 1) out vec2 outUv;
2425

2526
void main()
2627
{
@@ -29,6 +30,7 @@ void main()
2930
const vec4 worldPos = instance.model * vec4(aPos, 0.0, 1.0);
3031

3132
outColour = aColour * instance.tint;
33+
outUv = aUv;
3234

3335
gl_Position = matVP * worldPos;
3436
}

Lib/Src/Graphics/Renderer.cpp

+18-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ namespace Tkge::Graphics
5757
}
5858

5959
UpdateInstances(instances);
60-
if (!WriteSets()) { return; }
60+
if (!WriteSets(primitive.texture)) { return; }
6161

6262
_renderPass->get_command_buffer().setViewport(0, _viewport);
6363

@@ -74,16 +74,18 @@ namespace Tkge::Graphics
7474
}
7575
}
7676

77-
bool Renderer::WriteSets() const
77+
bool Renderer::WriteSets(const Texture* texture) const
7878
{
7979
auto& renderDevice = _renderPass->get_render_device();
80+
if (texture == nullptr) { texture = &_resourcePool->GetFallbackTexture(); }
8081

8182
const auto setLayouts = _resourcePool->SetLayouts();
8283
auto descriptorSets = std::array<vk::DescriptorSet, 1>{};
8384
KLIB_ASSERT(setLayouts.size() == descriptorSets.size()); // expected set count will change until render flow is stable
8485
if (!renderDevice.allocate_sets(descriptorSets, setLayouts)) { return false; }
8586

8687
auto bufferInfos = klib::FlexArray<vk::DescriptorBufferInfo, 4>{};
88+
auto imageInfos = klib::FlexArray<vk::DescriptorImageInfo, 2>{};
8789
auto descriptorWrites = klib::FlexArray<vk::WriteDescriptorSet, 8>{};
8890
const auto pushBufferWrite = [&](vk::DescriptorSet set, std::uint32_t binding, const Buffer& buffer, const vk::DescriptorType type)
8991
{
@@ -95,6 +97,18 @@ namespace Tkge::Graphics
9597
descriptorWrites.push_back(wds);
9698
};
9799

100+
const auto pushImageWrite = [&](vk::DescriptorSet set, std::uint32_t binding, const Texture& texture)
101+
{
102+
imageInfos.push_back({});
103+
auto& dii = imageInfos.back();
104+
dii.setImageView(texture.GetImage().get_view())
105+
.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
106+
.setSampler(_resourcePool->GetSampler(texture.sampler));
107+
auto wds = vk::WriteDescriptorSet{};
108+
wds.setImageInfo(dii).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eCombinedImageSampler).setDstSet(set).setDstBinding(binding);
109+
descriptorWrites.push_back(wds);
110+
};
111+
98112
auto& ubo00 = _resourcePool->AllocateBuffer(vk::BufferUsageFlagBits::eUniformBuffer, sizeof(glm::mat4));
99113
const auto halfRenderArea = 0.5f * glm::vec2{_viewport.width, -_viewport.height};
100114
const auto matProj = glm::ortho(-halfRenderArea.x, halfRenderArea.x, -halfRenderArea.y, halfRenderArea.y);
@@ -107,6 +121,8 @@ namespace Tkge::Graphics
107121
kvf::util::overwrite(ssbo01, instanceSpan);
108122
pushBufferWrite(descriptorSets[0], 1, ssbo01, vk::DescriptorType::eStorageBuffer);
109123

124+
pushImageWrite(descriptorSets[0], 2, *texture);
125+
110126
const auto writeSpan = std::span{descriptorWrites.data(), descriptorWrites.size()};
111127
renderDevice.get_device().updateDescriptorSets(writeSpan, {});
112128

Lib/Src/Graphics/Texture.cpp

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <Tkge/Graphics/Texture.hpp>
2+
#include <kvf/image_bitmap.hpp>
3+
#include <kvf/util.hpp>
4+
5+
namespace Tkge::Graphics
6+
{
7+
namespace
8+
{
9+
constexpr auto ImageCreateInfo = kvf::vma::ImageCreateInfo{
10+
.format = vk::Format::eR8G8B8A8Srgb,
11+
};
12+
13+
}
14+
15+
Texture::Texture(gsl::not_null<kvf::RenderDevice*> renderDevice) : _image(renderDevice, ImageCreateInfo) {}
16+
17+
bool Texture::Create(const kvf::Bitmap& bitmap)
18+
{
19+
if (bitmap.bytes.empty()) { return false; }
20+
return kvf::util::write_to(_image, bitmap);
21+
}
22+
23+
bool Texture::Decompress(std::span<const std::byte> compressedImage)
24+
{
25+
if (compressedImage.empty()) { return false; }
26+
const auto image = kvf::ImageBitmap{compressedImage};
27+
if (!image.is_loaded()) { return false; }
28+
return Create(image.bitmap());
29+
}
30+
31+
glm::ivec2 Texture::GetSize() const { return kvf::util::to_glm_vec<int>(_image.get_extent()); }
32+
} // namespace Tkge::Graphics

0 commit comments

Comments
 (0)