fixing put pixel resize issue

This commit is contained in:
2026-02-03 11:58:06 +01:00
parent 2d9f193a72
commit 7639c27df4
6 changed files with 172 additions and 19 deletions

View File

@@ -44,11 +44,16 @@ void update(void* param)
mlx_set_font(mlx->mlx, "default");
mlx_string_put(mlx->mlx, mlx->win, 20, 50, (mlx_color){ .rgba = 0xFFFFFFFF }, "that's a text");
int win_width;
int win_height;
mlx_get_window_size(mlx->mlx, mlx->win, &win_width, &win_height);
uint32_t color = 0;
for(int j = 0; j < 400; j++)
for(int j = 0; j < win_height; j++)
{
mlx_pixel_put(mlx->mlx, mlx->win, j, j, (mlx_color){ .rgba = 0x0000FFFF + (color << 24) });
mlx_pixel_put(mlx->mlx, mlx->win, 399 - j, j, (mlx_color){ .rgba = 0x0000FFFF });
mlx_pixel_put(mlx->mlx, mlx->win, win_width - j, j, (mlx_color){ .rgba = 0x0000FFFF });
color += (color < 255);
}

View File

@@ -8,7 +8,7 @@ namespace mlx
class PutPixelManager
{
public:
PutPixelManager(NonOwningPtr<class Renderer> renderer) : p_renderer(renderer) {}
PutPixelManager(NonOwningPtr<class Renderer> renderer);
// Returns a valid pointer when a new texture has been created
NonOwningPtr<Texture> DrawPixel(int x, int y, std::uint64_t draw_layer, mlx_color color);
@@ -20,6 +20,7 @@ namespace mlx
private:
NonOwningPtr<Texture> GetLayer(std::uint64_t draw_layer, bool& is_newlayer);
std::unique_ptr<Texture> NewTexture();
private:
std::unordered_map<std::uint64_t, NonOwningPtr<Texture>> m_placements;

View File

@@ -89,7 +89,13 @@ namespace mlx
mlx_color GetPixel(int x, int y) noexcept;
void GetRegion(int x, int y, int w, int h, mlx_color* dst) noexcept;
void Clear(VkCommandBuffer cmd, Vec4f color) override;
void CopyTo(Texture& other);
void Resize(std::uint32_t width, std::uint32_t height);
void Swap(Texture& texture) noexcept;
// If a valid cmd buffer is passed, this function takes ownership and makes it invalid after
void SyncCPUBuffer(VkCommandBuffer cmd = VK_NULL_HANDLE);
void Update(VkCommandBuffer cmd);
~Texture() override { Destroy(); }

View File

@@ -6,6 +6,29 @@
namespace mlx
{
PutPixelManager::PutPixelManager(NonOwningPtr<class Renderer> renderer) : p_renderer(renderer)
{
MLX_PROFILE_FUNCTION();
std::function<void(const EventBase&)> functor = [this](const EventBase& event)
{
// Suboptimal for multi-windows applications
if(event.What() == Event::ResizeEventCode)
{
VkExtent2D extent{ .width = 0, .height = 0 };
if(p_renderer->GetWindow())
extent = kvfGetSwapchainImagesSize(p_renderer->GetSwapchain().Get());
else if(p_renderer->GetRenderTarget())
extent = VkExtent2D{ .width = p_renderer->GetRenderTarget()->GetWidth(), .height = p_renderer->GetRenderTarget()->GetHeight() };
else
FatalError("a renderer was created without window nor render target attached (wtf!?)");
for(auto& texture : m_textures)
texture->Resize(extent.width, extent.height);
}
};
EventBus::RegisterListener({ functor, "mlx_put_pixel_manager_" + std::to_string(reinterpret_cast<std::uintptr_t>(this)) });
}
NonOwningPtr<Texture> PutPixelManager::DrawPixel(int x, int y, std::uint64_t draw_layer, mlx_color color)
{
MLX_PROFILE_FUNCTION();
@@ -53,21 +76,8 @@ namespace mlx
is_newlayer = true;
if(m_current_texture_index >= m_textures.size())
{
VkExtent2D extent{ .width = 0, .height = 0 };
if(p_renderer->GetWindow())
extent = kvfGetSwapchainImagesSize(p_renderer->GetSwapchain().Get());
else if(p_renderer->GetRenderTarget())
extent = VkExtent2D{ .width = p_renderer->GetRenderTarget()->GetWidth(), .height = p_renderer->GetRenderTarget()->GetHeight() };
else
FatalError("a renderer was created without window nor render target attached (wtf)");
m_textures.push_back(NewTexture());
#ifdef DEBUG
m_textures.push_back(std::make_unique<Texture>(CPUBuffer{}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_SRGB, false, "mlx_put_pixel_layer_" + std::to_string(m_current_texture_index)));
#else
m_textures.push_back(std::make_unique<Texture>(CPUBuffer{}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_SRGB, false, std::string_view{}));
#endif
}
try
{
m_placements[draw_layer] = m_textures.at(m_current_texture_index).get();
@@ -83,6 +93,23 @@ namespace mlx
}
}
std::unique_ptr<Texture> PutPixelManager::NewTexture()
{
VkExtent2D extent{ .width = 0, .height = 0 };
if(p_renderer->GetWindow())
extent = kvfGetSwapchainImagesSize(p_renderer->GetSwapchain().Get());
else if(p_renderer->GetRenderTarget())
extent = VkExtent2D{ .width = p_renderer->GetRenderTarget()->GetWidth(), .height = p_renderer->GetRenderTarget()->GetHeight() };
else
FatalError("a renderer was created without window nor render target attached (wtf!?)");
#ifdef DEBUG
return std::make_unique<Texture>(CPUBuffer{}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_SRGB, false, "mlx_put_pixel_layer_" + std::to_string(m_current_texture_index));
#else
return std::make_unique<Texture>(CPUBuffer{}, extent.width, extent.height, VK_FORMAT_R8G8B8A8_SRGB, false, std::string_view{});
#endif
}
void PutPixelManager::ResetRenderData()
{
m_placements.clear();

View File

@@ -24,7 +24,7 @@
namespace mlx
{
mlx_color ReverseColor(mlx_color color)
MLX_FORCEINLINE mlx_color ReverseColor(mlx_color color)
{
mlx_color reversed_color;
reversed_color.r = color.a;
@@ -362,8 +362,16 @@ namespace mlx
m_staging_buffer->Init(BufferType::Staging, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, {});
#endif
SyncCPUBuffer();
}
void Texture::SyncCPUBuffer(VkCommandBuffer cmd)
{
if(!m_staging_buffer.has_value())
return;
VkImageLayout old_layout = m_layout;
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
if(cmd == VK_NULL_HANDLE)
cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cmd);
kvfCopyImageToBuffer(cmd, m_staging_buffer->Get(), m_image, m_staging_buffer->GetOffset(), VK_IMAGE_ASPECT_COLOR_BIT, { m_width, m_height, 1 });
@@ -375,6 +383,98 @@ namespace mlx
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
}
void Texture::CopyTo(Texture& other)
{
VkImageLayout old_layout = m_layout;
VkImageLayout other_old_layout = other.GetLayout();
VkImageSubresourceLayers subresource{};
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource.mipLevel = 0;
subresource.layerCount = 1;
subresource.baseArrayLayer = 0;
VkExtent3D extent{};
extent.width = m_width;
extent.height = m_height;
extent.depth = 1;
VkOffset3D offset{};
offset.x = 0;
offset.y = 0;
offset.z = 0;
VkImageCopy region{};
region.srcSubresource = subresource;
region.dstSubresource = subresource;
region.extent = extent;
region.srcOffset = offset;
region.dstOffset = offset;
VkCommandBuffer cmd = kvfCreateCommandBuffer(RenderCore::Get().GetDevice());
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, cmd);
other.TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, cmd);
kvfCopyImageToImage(cmd, m_image, m_layout, other.Get(), other.GetLayout(), 1, &region);
TransitionLayout(old_layout, cmd);
other.TransitionLayout(other_old_layout, cmd);
kvfEndCommandBuffer(cmd);
SyncCPUBuffer(cmd);
}
void Texture::Resize(std::uint32_t width, std::uint32_t height)
{
#ifdef DEBUG
Texture new_texture = Texture(CPUBuffer{}, width, height, m_format, m_is_multisampled, m_debug_name);
#else
Texture new_texture = Texture(CPUBuffer{}, width, height, m_format, m_is_multisampled, std::string_view{});
#endif
if(m_staging_buffer.has_value())
new_texture.OpenCPUBuffer();
// Suboptimal operations, should bake all of them in a single command buffer
new_texture.Clear(VK_NULL_HANDLE, Vec4f{ 0.f });
CopyTo(new_texture);
Swap(new_texture);
}
void Texture::Swap(Texture& texture) noexcept
{
MLX_PROFILE_FUNCTION();
#ifdef DEBUG
std::swap(m_debug_name, texture.m_debug_name);
#endif
std::swap(m_allocation, texture.m_allocation);
std::swap(m_image, texture.m_image);
std::swap(m_image_view, texture.m_image_view);
std::swap(m_sampler, texture.m_sampler);
std::swap(m_format, texture.m_format);
std::swap(m_tiling, texture.m_tiling);
std::swap(m_layout, texture.m_layout);
std::swap(m_type, texture.m_type);
std::swap(m_width, texture.m_width);
std::swap(m_height, texture.m_height);
std::swap(m_is_multisampled, texture.m_is_multisampled);
if(m_staging_buffer.has_value() && texture.m_staging_buffer.has_value())
m_staging_buffer->Swap(*texture.m_staging_buffer);
else if(m_staging_buffer.has_value())
m_staging_buffer.reset();
else if(texture.m_staging_buffer.has_value())
texture.m_staging_buffer.reset();
m_has_been_modified = true;
texture.m_has_been_modified = true;
}
Texture* StbTextureLoad(const std::filesystem::path& file, int* w, int* h)
{
using namespace std::literals;

14
third_party/kvf.h vendored
View File

@@ -152,6 +152,8 @@ void kvfDestroySemaphore(VkDevice device, VkSemaphore semaphore);
VkImage kvfCreateImage(VkDevice device, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, KvfImageType type);
void kvfCopyImageToBuffer(VkCommandBuffer cmd, VkBuffer dst, VkImage src, size_t buffer_offset, VkImageAspectFlagBits aspect, VkExtent3D extent);
void kvfCopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImageLayout src_layout, VkImage dst, VkImageLayout dst_layout, uint32_t count, const VkImageCopy* regions);
void kvfDestroyImage(VkDevice device, VkImage image);
VkImageView kvfCreateImageView(VkDevice device, VkImage image, VkFormat format, VkImageViewType type, VkImageAspectFlags aspect, int layer_count);
void kvfDestroyImageView(VkDevice device, VkImageView image_view);
@@ -2147,6 +2149,18 @@ void kvfCopyImageToBuffer(VkCommandBuffer cmd, VkBuffer dst, VkImage src, size_t
KVF_GET_DEVICE_FUNCTION(vkCmdCopyImageToBuffer)(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, 1, &region);
}
void kvfCopyImageToImage(VkCommandBuffer cmd, VkImage src, VkImageLayout src_layout, VkImage dst, VkImageLayout dst_layout, uint32_t count, const VkImageCopy* regions)
{
KVF_ASSERT(cmd != VK_NULL_HANDLE);
KVF_ASSERT(dst != VK_NULL_HANDLE);
KVF_ASSERT(src != VK_NULL_HANDLE);
#ifdef KVF_IMPL_VK_NO_PROTOTYPES
__KvfDevice* kvf_device = __kvfGetKvfDeviceFromVkCommandBuffer(cmd);
KVF_ASSERT(kvf_device != NULL && "could not find VkDevice in registered devices");
#endif
KVF_GET_DEVICE_FUNCTION(vkCmdCopyImage)(cmd, src, src_layout, dst, dst_layout, count, regions);
}
void kvfDestroyImage(VkDevice device, VkImage image)
{
if(image == VK_NULL_HANDLE)