Link

๊ฐ์ฒด๋ฅผ ์ œ๋Œ€๋กœ ๋ Œ๋”๋งํ•˜๋ ค๋ฉด ์œ„ํ•ด ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์ •์  ์…ฐ์ด๋”๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ผ ์‚ผ๊ฐํ˜• ํ˜น์€ ๋‹จ์ˆœํ•œ ๋„ํ˜•์™ธ์—๋Š” ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๊ณ ์ • ํ•จ์ˆ˜ ๋ฐฉ์‹์˜ ์ •์  ์†์„ฑ ๊ฐ€์ ธ์˜ค๊ธฐ(fetch) ๋กœ์ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์…ฐ์ด๋”์—์„œ ์–ด๋–ป๊ฒŒ ์ •ํ™•ํžˆ ๋ถˆ๋Ÿฌ์˜ฌ์ง€๋Š” ์ „์ ์œผ๋กœ ์ž์œ ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ •์ ๋“ค์„ Buffer Device Address๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋œ ๋Œ€์šฉ๋Ÿ‰ GPU ๋ฒ„ํผ๋กœ๋ถ€ํ„ฐ ๋ถˆ๋Ÿฌ์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๊ณ ์„ฑ๋Šฅ๊ณผ ๋†’์€ ์œ ์—ฐ์„ฑ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.

Vulkan ๋ฒ„ํผ

Vulkan์—์„œ๋Š” ๋ฒ”์šฉ ๋ชฉ์ ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ฒ„ํผ๋ฅผ ํ†ตํ•ด ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ˜ํ”Œ๋Ÿฌ๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š๊ณ  ์ผ๋ฐ˜์ ์ธ CPU์ธก์˜ ๊ตฌ์กฐ์ฒด ํ˜น์€ ๋ฐฐ์—ด์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” ๋ฉด์—์„œ ๋ฒ„ํผ๋Š” ์ด๋ฏธ์ง€์™€๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด ํ˜น์€ ๊ตฌ์กฐ์ฒด ๋ฐฐ์—ด๋กœ ์ด๋ฅผ ์…ฐ์ด๋”์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฒ„ํผ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์šฉ๋„์— ๋งž๋Š” Usage ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์…ฐ์ด๋”์—๋Š” ๋ฒ”์šฉ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์—ฐ์‚ฐ์„ ์œ„ํ•œ ๋‘ ์ข…๋ฅ˜์˜ ๋ฒ„ํผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹ˆํผ ๋ฒ„ํผ์™€ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํผ์ž…๋‹ˆ๋‹ค.

์œ ๋‹ˆํผ ๋ฒ„ํผ๋Š” ์…ฐ์ด๋”์—์„œ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํฌ๊ธฐ๋Š” ์ œ์กฐ์‚ฌ๋งˆ๋‹ค ๋‹ค๋ฅด์ง€๋งŒ ์ตœ์†Œ 16KB๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ GPU๊ฐ€ ์‚ฌ์ „ ์บ์‹ฑ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ๋น ๋ฅธ ์ ‘๊ทผ ์†๋„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ํฌ๊ธฐ ์ œํ•œ์€ ์…ฐ์ด๋”์— ๋ฐ”์ธ๋”ฉ๋œ ๋ถ€๋ถ„์—๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•˜๋‚˜์˜ ์ปค๋‹ค๋ž€ ์œ ๋‹ˆํผ ๋ฒ„ํผ๋ฅผ ๋งŒ๋“ค์–ด ์ผ๋ถ€๋งŒ ์…ฐ์ด๋”๋กœ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋“œ์›จ์–ด์— ๋”ฐ๋ผ ํ‘ธ์‹œ์ƒ์ˆ˜๊ฐ€ ๋“œ๋ผ์ด๋ฒ„์— ์˜ํ•ด ์œ ๋‹ˆํผ ๋ฒ„ํผ์˜ ํ•œ ํ˜•ํƒœ๋กœ ๊ตฌํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํผ(SSBO)๋Š” ๋Œ€์šฉ๋Ÿ‰์˜ ์ฝ๊ธฐ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๋ฒ„ํผ์ž…๋‹ˆ๋‹ค. ์ตœ์†Œ ํฌ๊ธฐ๋Š” 128MB์ด๋ฉฐ, ์ด ํŠœํ† ๋ฆฌ์–ผ์—์„œ ํƒ€๊ฒŒํŒ…ํ•˜๋Š” ํ˜„๋Œ€ PC์˜ GPU๋Š” 4GB๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ํฌ๊ธฐ๋Š” uint32 ํฌ๊ธฐ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•œ ์ œํ•œ์ž…๋‹ˆ๋‹ค. ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํผ๋Š” ์œ ๋‹ˆํผ ๋ฒ„ํผ์ฒ˜๋Ÿผ ์‚ฌ์ „ ๋กœ๋”ฉ๋˜์ง€ ์•Š์œผ๋ฉฐ ๋ณด๋‹ค ์ผ๋ฐ˜์ ์ธ ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ/์ €์žฅ(load/store)์„ ์œ„ํ•œ ์šฉ๋„๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์œ ๋‹ˆํผ ๋ฒ„ํผ๋Š” ํฌ๊ธฐ๊ฐ€ ์ž‘๊ธฐ ๋•Œ๋ฌธ์— ์ •์  ๋„ํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ธฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋จธํ…Œ๋ฆฌ์–ผ ์ธ์ž์™€ ์ „์—ญ ์”ฌ ๊ตฌ์„ฑ์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐ์—๋Š” ๋งค์šฐ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

์œ ๋‹ˆํผ ๋ฒ„ํผ์™€ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํผ์˜ ์ •ํ™•ํ•œ ์†๋„ ์ฐจ์ด๋Š” GPU์™€ ์…ฐ์ด๋”์˜ ์ž‘์—… ๋‚ด์šฉ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๋†’์€ ์œ ์—ฐ์„ฑ์„ ์œ„ํ•ด ์Šคํ† ๋ฆฌ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ณดํŽธ์ ์ž…๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ํ”„๋กœ์ ํŠธ์— ์‹ค์งˆ์ ์ธ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ๋ฒค์น˜๋งˆํฌ์—์„œ ๋ฒ„ํผ์— ์ ‘๊ทผํ•˜๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://github.com/sebbbi/perftest .

๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋™์  ๋ฒ„ํผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋™์  ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ช…๋ น์„ ๊ธฐ๋กํ•  ๋•Œ ๋ฒ„ํผ๊ฐ€ ๋ฐ”์ธ๋”ฉ๋˜๋Š” ์˜คํ”„์…‹์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์„ ํ™œ์šฉํ•˜๋ฉด ํ•˜๋‚˜์˜ ์ปค๋‹ค๋ž€ ๋ฒ„ํผ์— ์—ฌ๋Ÿฌ ์˜ค๋ธŒ์ ํŠธ์˜ ์œ ๋‹ˆํผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ  ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹ ํ•˜๋‚˜๋กœ ๋‹ค์–‘ํ•œ ์˜คํ”„์…‹์„ ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์œ ๋‹ˆํผ ๋ฒ„ํผ์™€ ์ž˜ ์–ด์šธ๋ฆฌ์ง€๋งŒ ์Šคํ† ๋ฆฌ์ง€ ๋ฒ„ํผ์—๋Š” ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ์“ฐ๋Š” ํŽธ์ด ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

Buffer Device Address

์ผ๋ฐ˜์ ์œผ๋กœ ๋ฒ„ํผ๋Š” ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์„ ํ†ตํ•ด ๋ฐ”์ธ๋”ฉํ•ด์•ผ ํ•˜๋ฉฐ, ์ด๋Š” CPU์—์„œ ํŠน์ • ๋ฒ„ํผ์˜ ํฌ๊ธฐ๋‚˜ ๋ ˆ์ด์•„์›ƒ์„ ๋ฏธ๋ฆฌ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•˜๊ณ , ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์˜ ์ˆ˜๋ช… ๊ด€๋ฆฌ๋„ ํ•จ๊ป˜ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Vulkan 1.3์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ„ํผ ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ผ๋Š” ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” int64ํฌ์ธํ„ฐ๋ฅผ GPU๋กœ ์ „๋‹ฌํ•˜์—ฌ ์…ฐ์ด๋”์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉฐ ํฌ์ธํ„ฐ ์—ฐ์‚ฐ๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. C++ ํฌ์ธํ„ฐ์™€ ๋™์ผํ•œ ๋งค์ปค๋‹ˆ์ฆ˜์ด๋ฉฐ ๋งํฌ๋“œ ๋ฆฌ์ŠคํŠธ๋‚˜ ๊ฐ„์ ‘(indirect) ์ ‘๊ทผ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๋„ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ •์  ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์—์‚ฌ์šฉํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ํ†ตํ•œ SSBO ์ ‘๊ทผ์€ ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์„ ํ†ตํ•œ ์ ‘๊ทผ๋ณด๋‹ค ๋” ๋น ๋ฅด๋ฉฐ, ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์…ฐ์ด๋”์— ๋ฐ”์ธ๋”ฉํ•  ๋•Œ ํ‘ธ์‹œ์ƒ์ˆ˜๋ฅผ ํ†ตํ•ด ๋งค์šฐ ๋น ๋ฅด๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฒ„ํผ ์ƒ์„ฑํ•˜๊ธฐ

๋ฉ”์‹œ๋ฅผ GPU๋กœ ์—…๋กœ๋“œ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•  ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ vk_types.h์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

struct AllocatedBuffer {
    VkBuffer buffer;
    VmaAllocation allocation;
    VmaAllocationInfo info;
};

์ด ๊ตฌ์กฐ์ฒด๋Š” ์ฃผ์–ด์ง„ ๋ฒ„ํผ์™€ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์„ ๋•Œ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. Vulkan ํ•ธ๋“ค์ธ VkBuffer์™€ VmaAllocation, ๊ทธ๋ฆฌ๊ณ  ๋ฒ„ํผ๋ฅผ ํ•ด์ œํ•  ๋•Œ ํ•„์š”ํ•œ ๋ฒ„ํผ์™€ ๋ฒ„ํผ ํ• ๋‹น์— ๊ด€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” VmaAllocationInfo๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

VulkanEngine์— ์ด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);

ํ• ๋‹น ํฌ๊ธฐ์™€ usage ํ”Œ๋ž˜๊ทธ, ๊ทธ๋ฆฌ๊ณ  VmaMemoryUsage ํ”Œ๋ž˜๊ทธ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ๋ฒ„ํผ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์–ด๋””์— ํ• ๋‹น๋  ์ง€๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค

์•„๋ž˜๋Š” ๊ตฌํ˜„๋ถ€์ž…๋‹ˆ๋‹ค.

AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage)
{
	// allocate buffer
	VkBufferCreateInfo bufferInfo = {.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
	bufferInfo.pNext = nullptr;
	bufferInfo.size = allocSize;

	bufferInfo.usage = usage;

	VmaAllocationCreateInfo vmaallocInfo = {};
	vmaallocInfo.usage = memoryUsage;
	vmaallocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
	AllocatedBuffer newBuffer;

	// allocate the buffer
	VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, &vmaallocInfo, &newBuffer.buffer, &newBuffer.allocation,
		&newBuffer.info));

	return newBuffer;
}

๋จผ์ € VkBuffeCreateInfo ๊ตฌ์กฐ์ฒด๋ฅผ ์ฑ„์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ์ฒด์—๋Š” ๋ฒ„ํผ์˜ ํฌ๊ธฐ์™€ Usage ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„ VMA๊ฐ€ ์‚ฌ์šฉํ•  ์†์„ฑ๋“ค์„ ๋‹ด๋Š” AllocationCreateInfo๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ VmaMemoryUsageํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ VMA๊ฐ€ ๋ฒ„ํผ๋ฅผ ์–ด๋–ค ๋ฉ”๋ชจ๋ฆฌ ํƒ€์ž…์— ๋ฐฐ์น˜ํ•  ์ง€ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€์˜ ๊ฒฝ์šฐ GPU์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ๋น ๋ฅธ ๊ณต๊ฐ„์ธ ๋””๋ฐ”์ด์Šค ์ „์šฉ ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฒ„ํผ์—์„œ๋Š” CPU์—์„œ ์ง์ ‘ ์“ธ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ฃผ ์‚ฌ์šฉ ๋ชฉ์ ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

  • VMA_MEMORY_USAGE_GPU_ONLY๋Š” GPU ์ „์šฉ ๋ฉ”๋ชจ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์ด ๋ฉ”๋ชจ๋ฆฌ๋Š” GPU VRAM ๊ณต๊ฐ„์— ์œ„์น˜ํ•˜๋ฏ€๋กœ CPU์—์„œ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์—†์ง€๋งŒ, ์…ฐ์ด๋”์—์„œ ์ฝ๊ณ  ์“ฐ๊ธฐ๊ฐ€ ๊ฐ€์žฅ ๋น ๋ฆ…๋‹ˆ๋‹ค.
  • VMA_MEMORY_USAGE_CPU_ONLY๋Š” CPU RAM์— ์žˆ๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค. ์ด๋Š” CPU์—์„œ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ GPU๋Š” ์ฝ๊ธฐ๋งŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ CPU RAM์ด GPU ์™ธ๋ถ€์— ์žˆ์–ด์„œ ๊ทธ๋Ÿฐ ๊ฒƒ์ด๋ผ๋Š” ์‚ฌ์‹ค์„ ๋ช…์‹ฌํ•˜์„ธ์š”. GPU์—์„œ RAM์— ์ ‘๊ทผํ•  ๋•Œ์—๋Š” ์„ฑ๋Šฅ ์†์‹ค์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ์ดํ„ฐ๋‚˜, ์ ‘๊ทผ ์†๋„๊ฐ€ ํฌ๊ฒŒ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ์†Œ๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • VMA_MEMORY_USAGE_CPU_TO_GPU๋Š” CPU์—์„œ ์“ฐ๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ , GPU์—์„œ๋„ ๋น„๊ต์  ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ์ž…๋‹ˆ๋‹ค. Vulkan 1.2๋ฒ„์ „ ์ด์ƒ์—์„œ๋Š” GPU๊ฐ€ ์ž์ฒด VRAM ๋‚ด์— CPU์—์„œ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ์ž‘์€ ์˜์—ญ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์—ฌ๊ธฐ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. Resizable BAR๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•œ ์šฉ๋Ÿ‰์€ ์ œํ•œ์ ์ด์ง€๋งŒ, CPU์—์„œ ์ง์ ‘ ์“ฐ๊ณ  GPU์—์„œ๋„ ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • VMA_MEMORY_USAGE_GPU_TO_CPU๋Š” CPU์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

VMA_ALLOCATION_CREATE_MAPPED_BIT์„ ๋ชจ๋“  ๋ฒ„ํผ ํ• ๋‹น์— ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” VMA๊ฐ€ ํฌ์ธํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘ํ•˜์—ฌ ๋ฒ„ํผ๊ฐ€ CPU์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ๋ฐ”๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. VMA๋Š” ์ด ํฌ์ธํ„ฐ๋ฅผ allocationInfo์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๋ฒ„ํผ ์ƒ์„ฑ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋ฏ€๋กœ ํŒŒ๊ดด ํ•จ์ˆ˜๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํผ๋ฅผ ํŒŒ๊ดดํ•  ๋•Œ์—๋Š” vmaDestroyBuffer๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer)
{
    vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation);
}

์ด๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ ๊ตฌ์กฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ •์  ๋ฒ„ํผ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GPU์—์„œ์˜ ๋ฉ”์‹œ ๋ฒ„ํผ

vk_types.h

struct Vertex {

	glm::vec3 position;
	float uv_x;
	glm::vec3 normal;
	float uv_y;
	glm::vec4 color;
};

// holds the resources needed for a mesh
struct GPUMeshBuffers {

    AllocatedBuffer indexBuffer;
    AllocatedBuffer vertexBuffer;
    VkDeviceAddress vertexBufferAddress;
};

// push constants for our mesh object draws
struct GPUDrawPushConstants {
    glm::mat4 worldMatrix;
    VkDeviceAddress vertexBuffer;
};

์šฐ๋ฆฌ๋Š” ์ •์  ํฌ๋งท์ด ํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋Š” ์ด ํฌ๋งท์„ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ •์  ํฌ๋งท์„ ์ •์˜ํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋Šฅํ•œ ํ•œ ์••์ถ•ํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•˜์ง€๋งŒ, ํ˜„์žฌ ํŠœํ† ๋ฆฌ์–ผ ๋‹จ๊ณ„์—์„œ๋Š” ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ ์šฐ์„ ์€ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ๋‹จ๊ณ„์—์„œ ์ด ์ •์  ํฌ๋งท์„ ์ตœ์ ํ™”ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. UV ๋ณ€์ˆ˜๊ฐ€ ์ค‘๊ฐ„์— ์‚ฝ์ž…๋œ ์ด์œ ๋Š” GPU์˜ ์ •๋ ฌ ์ œํ•œ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ์ฒด๋ฅผ ์…ฐ์ด๋”์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ์™€ ๋™์ผํ•˜๊ฒŒ ๋งž์ถ”๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฐ ๋ฐฉ์‹์˜ ํฌ๋งท์ด ์„ฑ๋Šฅ์ด๋‚˜ ํ˜ธํ™˜์„ฑ ์ธก๋ฉด์—์„œ ๋” ๋ฐ”๋žŒ์งํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”์‹œ ๋ฐ์ดํ„ฐ๋ฅผ GPUMeshBuffers ๊ตฌ์กฐ์ฒด์— ๋‹ด์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ์ฒด๋Š” ์ •์ ๊ณผ ์ธ๋ฑ์Šค ๋ฒ„ํผ ๋ชจ๋‘๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ์ •์  ๋ฒ„ํผ์˜ ๋ฒ„ํผ ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”์‹œ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ ํ•„์š”ํ•œ ํ‘ธ์‹œ์ƒ์ˆ˜ ๊ตฌ์กฐ์ฒด๋„ ์ •์˜ํ•  ๊ฒƒ์ด๋ฉฐ, ์ด ๊ตฌ์กฐ์ฒด์—๋Š” ๊ฐ์ฒด์˜ ๋ณ€ํ™˜ ํ–‰๋ ฌ๊ณผ ๋ฉ”์‹œ ๋ฒ„ํผ์˜ ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ๋‹ด์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  GPU์— ์—…๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

GPUMeshBuffers VulkanEngine::uploadMesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
{
	const size_t vertexBufferSize = vertices.size() * sizeof(Vertex);
	const size_t indexBufferSize = indices.size() * sizeof(uint32_t);

	GPUMeshBuffers newSurface;

	//create vertex buffer
	newSurface.vertexBuffer = create_buffer(vertexBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
		VMA_MEMORY_USAGE_GPU_ONLY);

	//find the adress of the vertex buffer
	VkBufferDeviceAddressInfo deviceAdressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,.buffer = newSurface.vertexBuffer.buffer };
	newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAdressInfo);

	//create index buffer
	newSurface.indexBuffer = create_buffer(indexBufferSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
		VMA_MEMORY_USAGE_GPU_ONLY);

}

์ด๋ฅผ VulkanEngine ํด๋ž˜์Šค ์„ ์–ธ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ์ •์  ๋ฐ์ดํ„ฐ๋ฅผ ์œ„ํ•œ Vertex ํƒ€์ž…์˜ std::span๊ณผ ์ธ๋ฑ์Šค๋ฅผ ์œ„ํ•œ int ํƒ€์ž…์˜ std::span์„ ์ธ์ž๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. std::span์€ ํฌ์ธํ„ฐ์™€ ํฌ๊ธฐ๋กœ ๊ตฌ์„ฑ๋œ ๊ตฌ์กฐ๋กœ, C ์Šคํƒ€์ผ์˜ ๋ฐฐ์—ด์ด๋‚˜ std::vector์—์„œ ๋ณ€ํ™˜ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ํ•„์š”ํ•œ ๋ฒ„ํผ๊ฐ€ ์–ผ๋งˆ๋‚˜ ํฐ์ง€ ๊ณ„์‚ฐํ•˜๊ณ , GPU ์ „์šฉ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ •์  ๋ฒ„ํผ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ Usage ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. SSBO๋ฅผ ์œ„ํ•ดVK_BUFFER_USAGE_STORAGE_BUFFER_BIT๋ฅผ, ๋ฒ„ํผ ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•ด VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์ธ๋ฑ์Šค ๋ฒ„ํผ์—๋Š” VK_BUFFER_USAGE_INDEX_BUFFER_BIT๋ฅผ ์‚ฌ์šฉํ•ด ํ•ด๋‹น ๋ฒ„ํผ๋ฅผ ์ธ๋ฑ์Šค ๋“œ๋กœ์šฐ ํ˜ธ์ถœ์— ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ผ๊ณ  Vulkan์—๊ฒŒ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‘ ๋ฒ„ํผ ๋ชจ๋‘์— VK_BUFFER_USAGE_TRANSFER_DST_BIT๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ์ด๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ ๋ช…๋ น์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค.

๋ฒ„ํผ ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด vkGetBufferDeviceAddress๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.์ฃผ์†Œ๋ฅผ ์กฐํšŒํ•˜๊ณ ์ž ํ•˜๋Š” VkBuffer๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. VkDeviceAddress๋ฅผ ์–ป๊ณ  ๋‚˜๋ฉด, ํฌ์ธํ„ฐ ์—ฐ์‚ฐ๋„ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์ด๋Š” ์ปค๋‹ค๋ž€ ๋ฒ„ํผ์— ํ•˜์œ„ํ• ๋‹น์„ ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ฒ„ํผ๋ฅผ ํ• ๋‹นํ•œ ์ดํ›„์—๋Š” ๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Vulkan์—์„œ ํ”ํžˆ ์‚ฌ์šฉ๋˜๋Š” ๋ฐฉ์‹์ธ ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. GPU_ONLY ๋ฉ”๋ชจ๋ฆฌ๋Š” CPU์—์„œ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋จผ์ € CPU ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ์ž„์‹œ ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ์˜ ๋ฉ”๋ชจ๋ฆฌ์— ์ž‘์„ฑํ•˜๊ณ , ์ดํ›„ ๋ณต์‚ฌ ๋ช…๋ น์„ ํ†ตํ•ด GPU ์ „์šฉ ๋ฒ„ํผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. mesh์— ๊ผญ GPU_ONLY ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, CPU์—์„œ ํŒŒํ‹ฐํด ์‹œ์Šคํ…œ์ด๋‚˜ ๋‹ค๋ฅธ ๋™์  ํšจ๊ณผ๊ฐ€ ์•„๋‹Œ ํ•œ GPU_ONLY ๋ฒ„ํผ๋ฅผ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

	AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);

	void* data = staging.allocation->GetMappedData();

	// copy vertex buffer
	memcpy(data, vertices.data(), vertexBufferSize);
	// copy index buffer
	memcpy((char*)data + vertexBufferSize, indices.data(), indexBufferSize);

	immediate_submit([&](VkCommandBuffer cmd) {
		VkBufferCopy vertexCopy{ 0 };
		vertexCopy.dstOffset = 0;
		vertexCopy.srcOffset = 0;
		vertexCopy.size = vertexBufferSize;

		vkCmdCopyBuffer(cmd, staging.buffer, newSurface.vertexBuffer.buffer, 1, &vertexCopy);

		VkBufferCopy indexCopy{ 0 };
		indexCopy.dstOffset = 0;
		indexCopy.srcOffset = vertexBufferSize;
		indexCopy.size = indexBufferSize;

		vkCmdCopyBuffer(cmd, staging.buffer, newSurface.indexBuffer.buffer, 1, &indexCopy);
	});

	destroy_buffer(staging);

	return newSurface;

๋จผ์ € ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฒ„ํผ ํ•˜๋‚˜๋กœ ์ธ๋ฑ์Šค ๋ฒ„ํผ์™€ ์ •์  ๋ฒ„ํผ๋กœ์˜ ๋ณต์‚ฌ๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ์˜ ๋ฉ”๋ชจ๋ฆฌ ํƒ€์ž…์€ CPU_ONLY ์ด๋ฉฐ Usage ํ”Œ๋ž˜๊ทธ๋Š” ๋ณต์‚ฌ๋งŒ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— VK_BUFFER_USAGE_TRANSFER_SRC_BIT์ž…๋‹ˆ๋‹ค.

๋ฒ„ํผ๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด GetMappedData()๋ฅผ ํ†ตํ•ด ๋งคํ•‘๋œ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” void* ํ˜•ํƒœ์˜ ํฌ์ธํ„ฐ๋กœ, ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. memcpy๋ฅผ ๋‘ ๋ฒˆ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ span์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์Šคํ…Œ์ด์ง• ๋ฒ„ํผ๊ฐ€ ์ž‘์„ฑ๋˜๋ฉด immediate_submit์„ ์‹คํ–‰ํ•ด GPU ์ธก์—์„œ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•˜๋Š” ๋ช…๋ น์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ช…๋ น์€ 2๊ฐœ์˜ VkCmdCopyBuffer ๋ช…๋ น์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” GPU์—์„œ ์ˆ˜ํ–‰๋˜๋Š” memcpy์—ญํ• ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋œ VkBufferCopy ๊ตฌ์กฐ์ฒด๋Š” ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์“ธ ๋•Œ ์ˆ˜ํ–‰ํ–ˆ๋˜ memcpy๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ•˜๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

immediate submit์ด ์ˆ˜ํ–‰๋˜๋ฉด ์•ˆ์ „ํ•˜๊ฒŒ ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ๋ฅผ ํ•ด์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GPU ๋ช…๋ น์„ ์™„์ „ํžˆ ์‹คํ–‰๋  ๋•Œ ๊นŒ์ง€ CPU ์ธก์ด ๋Œ€๊ธฐํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ํŒจํ„ด์ด ํšจ์œจ์ ๋งŒ์€ ์•Š๋‹ค๋Š” ์ ์„ ๋ช…์‹ฌํ•˜์„ธ์š”. ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ ์—…๋กœ๋“œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์“ฐ๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ํŽธ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋ฉ”์‹œ ๊ทธ๋ฆฌ๊ธฐ

์ง€๊ธˆ๊นŒ์ง€ ๋งŒ๋“  ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•ด ๋ฉ”์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹ค์ œ๋กœ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•ด ๋ด…์‹œ๋‹ค. ์‚ผ๊ฐํ˜•์„ ์กฐํ•ฉํ•˜์—ฌ ์ธ๋ฑ์‹ฑ๋œ ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ •์  ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์œผ๋ฏ€๋กœ, ์…ฐ์ด๋”๋„ ์ด์— ๋งž๊ฒŒ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ž˜๊ทธ๋จผํŠธ ์…ฐ์ด๋”๋Š” ๊ธฐ์กด๊ณผ ๋™์ผํ•˜๊ฒŒ colored_triangle.frag๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฉฐ, ์ •์  ์…ฐ์ด๋”๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘ธ์‹œ ์ƒ์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒˆ๋กœ์šด ์ •์  ์…ฐ์ด๋”๋Š” ๊ธฐ์กด์˜ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์‚ผ๊ฐํ˜• ์…ฐ์ด๋”์™€ ์œ ์‚ฌํ•˜๋ฏ€๋กœ, colored_triangle_mesh.vert๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

#version 450
#extension GL_EXT_buffer_reference : require

layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 outUV;

struct Vertex {

	vec3 position;
	float uv_x;
	vec3 normal;
	float uv_y;
	vec4 color;
}; 

layout(buffer_reference, std430) readonly buffer VertexBuffer{ 
	Vertex vertices[];
};

//push constants block
layout( push_constant ) uniform constants
{	
	mat4 render_matrix;
	VertexBuffer vertexBuffer;
} PushConstants;

void main() 
{	
	//load vertex data from device adress
	Vertex v = PushConstants.vertexBuffer.vertices[gl_VertexIndex];

	//output data
	gl_Position = PushConstants.render_matrix *vec4(v.position, 1.0f);
	outColor = v.color.xyz;
	outUV.x = v.uv_x;
	outUV.y = v.uv_y;
}

GL_EXT_buffer_reference ํ™•์žฅ์„ ํ™œ์„ฑํ™”ํ•ด์•ผ ์…ฐ์ด๋” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฒ„ํผ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ํ›„ ์ •์  ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” CPU์—์„œ์˜ ๊ตฌ์กฐ์ฒด์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

์ดํ›„ ์ •์  ๋ฒ„ํผ๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฒ„ํผ๋Š” ์ •์  ๊ตฌ์กฐ์ฒด (unsized) ๋ฐฐ์—ด์„ ๊ฐ–๋Š” ์ฝ๊ธฐ ์ „์šฉ ๋ฒ„ํผ์ž…๋‹ˆ๋‹ค. layout์— buffer_reference๋ฅผ ์„ ์–ธํ•จ์œผ๋กœ์„œ ์…ฐ์ด๋”์—๊ฒŒ ํ•ด๋‹น ๊ตฌ์กฐ์ฒด๊ฐ€ ๋ฒ„ํผ ์ฃผ์†Œ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ๋จ์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. std430์€ GPU์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ ๊ทœ์น™์ž…๋‹ˆ๋‹ค.

ํ‘ธ์‹œ ์ƒ์ˆ˜ ๋ธ”๋Ÿญ์—๋Š” ์ •์  ์…ฐ์ด๋”์˜ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค์™€ ํ–‰๋ ฌ์„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ •์  ๋ฒ„ํผ๊ฐ€ buffer_reference๋กœ ์„ ์–ธ๋˜์—ˆ์œผ๋ฏ€๋กœ ์ด๋Š” ์‹ค์ œ๋กœ๋Š” uint64 ํ•ธ๋“ค์ด๋ฉฐ, ํ–‰๋ ฌ์€ ์ผ๋ฐ˜์ ์ธ ํ–‰๋ ฌ์ž…๋‹ˆ๋‹ค.

main()์—๋Š” gl_VertexIndex๋ฅผ ์‚ฌ์šฉํ•ด ์ •์  ๋ฐฐ์—ด์„ ์ธ๋ฑ์‹ฑํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฐฐ์—ด์—์„œ ์ˆ˜ํ–‰ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. GLSL์—์„œ๋Š” ๋ฒ„ํผ ์ฐธ์กฐ๋„ ์ผ๋ฐ˜ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์— C++ ์ฒ˜๋Ÿผ ->๊ฐ€ ์•„๋‹ˆ๋ผ .์„ ์‚ฌ์šฉํ•ด ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค. ์ •์ ์„ ์–ป์€ ํ›„์—๋Š” ํ•ด๋‹น ์ •์ ์˜ ์ƒ‰์ƒ๊ณผ ์œ„์น˜ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์œ„์น˜๋Š” ๋ Œ๋”๋ง ํ–‰๋ ฌ๊ณผ ๊ณฑํ•ด์ ธ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ƒ์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ init_triangle_pipeline()๊ณผ๋Š” ๋ณ„๋„๋กœ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋งŒ๋“ค์ง€๋งŒ ๊ตฌ์กฐ๋Š” ๊ฑฐ์˜ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. VulkanEngine์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

VkPipelineLayout _meshPipelineLayout;
VkPipeline _meshPipeline;

GPUMeshBuffers rectangle;

void init_mesh_pipeline();

init_triangle_pipeline()์˜ ๊ฑฐ์˜ ๋ณต์‚ฌ๋ณธ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

	VkShaderModule triangleFragShader;
	if (!vkutil::load_shader_module("../../shaders/colored_triangle.frag.spv", _device, &triangleFragShader)) {
		fmt::print("Error when building the triangle fragment shader module");
	}
	else {
		fmt::print("Triangle fragment shader succesfully loaded");
	}

	VkShaderModule triangleVertexShader;
	if (!vkutil::load_shader_module("../../shaders/colored_triangle_mesh.vert.spv", _device, &triangleVertexShader)) {
		fmt::print("Error when building the triangle vertex shader module");
	}
	else {
		fmt::print("Triangle vertex shader succesfully loaded");
	}

	VkPushConstantRange bufferRange{};
	bufferRange.offset = 0;
	bufferRange.size = sizeof(GPUDrawPushConstants);
	bufferRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;

	VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
	pipeline_layout_info.pPushConstantRanges = &bufferRange;
	pipeline_layout_info.pushConstantRangeCount = 1;

	VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_meshPipelineLayout));

์ •์  ์…ฐ์ด๋”๋ฅผ colored_triangle_mesh.vert.spv๋กœ ๊ต์ฒดํ•˜๊ณ , ์œ„์—์„œ ์ •์˜ํ•œ ํ‘ธ์‹œ ์ƒ์ˆ˜ ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํŒŒ์ดํ”„๋ผ์ธ ๋ ˆ์ด์•„์›ƒ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋จธ์ง€ ํ•จ์ˆ˜ ๋‚ด์šฉ์€ triangle pipeline ํ•จ์ˆ˜์™€ ๋™์ผํ•˜์ง€๋งŒ, ํŒŒ์ดํ”„๋ผ์ธ ๋ ˆ์ด์•„์›ƒ๊ณผ ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ์ฒด ์ด๋ฆ„์„ ์ƒˆ๋กœ์šด ๊ฒƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

	PipelineBuilder pipelineBuilder;

	//use the triangle layout we created
	pipelineBuilder._pipelineLayout = _meshPipelineLayout;
	//connecting the vertex and pixel shaders to the pipeline
	pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader);
	//it will draw triangles
	pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
	//filled triangles
	pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL);
	//no backface culling
	pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
	//no multisampling
	pipelineBuilder.set_multisampling_none();
	//no blending
	pipelineBuilder.disable_blending();

	pipelineBuilder.disable_depthtest();

	//connect the image format we will draw into, from draw image
	pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
	pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED);

	//finally build the pipeline
	_meshPipeline = pipelineBuilder.build_pipeline(_device);

	//clean structures
	vkDestroyShaderModule(_device, triangleFragShader, nullptr);
	vkDestroyShaderModule(_device, triangleVertexShader, nullptr);

	_mainDeletionQueue.push_function([&]() {
		vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr);
		vkDestroyPipeline(_device, _meshPipeline, nullptr);
	});

์ด์ œ init_pipelines()ํ•จ์ˆ˜์—์„œ ์ด๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

void VulkanEngine::init_pipelines()
{
	//COMPUTE PIPELINES	
	init_background_pipelines();

	// GRAPHICS PIPELINES
	init_triangle_pipeline();
	init_mesh_pipeline();
}

๋‹ค์Œ์€ ๋ฉ”์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์—…๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์—”์ง„์˜ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์„ ์ƒˆ๋กœ์šด ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ init_default_data()๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ init ํ•จ์ˆ˜์˜ ๋งˆ์ง€๋ง‰์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

void VulkanEngine::init_default_data() {
	std::array<Vertex,4> rect_vertices;

	rect_vertices[0].position = {0.5,-0.5, 0};
	rect_vertices[1].position = {0.5,0.5, 0};
	rect_vertices[2].position = {-0.5,-0.5, 0};
	rect_vertices[3].position = {-0.5,0.5, 0};

	rect_vertices[0].color = {0,0, 0,1};
	rect_vertices[1].color = { 0.5,0.5,0.5 ,1};
	rect_vertices[2].color = { 1,0, 0,1 };
	rect_vertices[3].color = { 0,1, 0,1 };

	std::array<uint32_t,6> rect_indices;

	rect_indices[0] = 0;
	rect_indices[1] = 1;
	rect_indices[2] = 2;

	rect_indices[3] = 2;
	rect_indices[4] = 1;
	rect_indices[5] = 3;

	rectangle = uploadMesh(rect_indices,rect_vertices);

	//delete the rectangle data on engine shutdown
	_mainDeletionQueue.push_function([&](){
		destroy_buffer(rectangle.indexBuffer);
		destroy_buffer(rectangle.vertexBuffer);
	});

}

์ •์ ๊ณผ ์ธ๋ฑ์Šค๋ฅผ ์œ„ํ•œ 2๊ฐœ์˜ ๋ฐฐ์—ด์„ ๋งŒ๋“ค๊ณ  uploadMesh ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ฒ„ํผ๋กœ ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. draw_geometry() ํ•จ์ˆ˜์—์„œ ๊ธฐ์กด์˜ ์‚ผ๊ฐํ˜•์„ ๊ทธ๋ฆฐ ๋’ค, ๊ทธ ์•„๋ž˜์— ์ƒˆ๋กœ์šด ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

	//launch a draw command to draw 3 vertices
	vkCmdDraw(cmd, 3, 1, 0, 0);

	vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline);

	GPUDrawPushConstants push_constants;
	push_constants.worldMatrix = glm::mat4{ 1.f };
	push_constants.vertexBuffer = rectangle.vertexBufferAddress;

	vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &push_constants);
	vkCmdBindIndexBuffer(cmd, rectangle.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);

	vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0);

	vkCmdEndRendering(cmd);

์ด๋ฒˆ์—๋Š” ์‚ฌ๊ฐํ˜• ๋ฉ”์‹œ๋ฅผ ์œ„ํ•œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ํ›„, ํ‘ธ์‹œ ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด vertexBufferAddress๋ฅผ GPU๋กœ ์—…๋กœ๋“œ ํ•ฉ๋‹ˆ๋‹ค. ํ–‰๋ ฌ์€ ๋ฉ”์‹œ ๋ณ€ํ™˜์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ๊นŒ์ง€๋Š” ๊ธฐ๋ณธ ํ–‰๋ ฌ์„ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ ํ›„ vkCmdBindIndexBuffer๋ฅผ ์‚ฌ์šฉํ•ด ์ธ๋ฑ์Šค ๋ฒ„ํผ๋ฅผ ๋ฐ”์ธ๋”ฉํ•ฉ๋‹ˆ๋‹ค. ์•„์‰ฝ๊ฒŒ๋„ ์—ฌ๊ธฐ์„œ๋Š” ๋””๋ฐ”์ด์Šค ์–ด๋“œ๋ ˆ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ , ๋Œ€์‹  VkBuffer์™€ ์˜คํ”„์…‹์„ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ vkCmdDrawIndexed๋ฅผ ์‚ฌ์šฉํ•ด 2๊ฐœ์˜ ์‚ผ๊ฐํ˜•(6๊ฐœ์˜ ์ธ๋ฑ์Šค)๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ์ด๋Š” vkCmdDraw์™€ ๊ฐ™์ง€๋งŒ ํ˜„์žฌ ๋ฐ”์ธ๋”ฉ๋œ ์ธ๋ฑ์Šค ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์‹œ๋ฅผ ๊ทธ๋ฆฐ๋‹ค๋Š” ์ ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

์ด๊ฒƒ์œผ๋กœ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์–ด๋–ค ๋ฉ”์‹œ๋„ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋ฐ˜์ ์ธ ์ˆ˜๋‹จ์ด ๋งˆ๋ จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ GLTF๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์™€ ์‚ฌ๊ฐํ˜•๋ณด๋‹ค๋Š” ๋” ๋ณต์žกํ•˜๊ณ  ๋ฉ‹์žˆ๋Š” ๊ฒƒ์„ ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Mesh Loading