diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp
index d430216f864ac3e7c5e4d4fa1f6339710b64cb93..9f086bdf5b335e08373c20f9e9c6da80a54882a6 100644
--- a/projects/fire_works/src/main.cpp
+++ b/projects/fire_works/src/main.cpp
@@ -1,6 +1,7 @@
 
 #include <array>
 
+#include <vkcv/Buffer.hpp>
 #include <vkcv/Core.hpp>
 #include <vkcv/DrawcallRecording.hpp>
 
@@ -186,7 +187,7 @@ void InitializeNestedFireworkEvents(std::vector<event_t>& events) {
 }
 
 void ChangeColor(std::vector<event_t>& events, glm::vec3 color) {
-	for (int i = 0; i < events.size(); i++) {
+	for (size_t i = 0; i < events.size(); i++) {
 		events [i].color = color;
 	}
 }
@@ -195,6 +196,9 @@ int main(int argc, const char **argv) {
 	vkcv::Features features;
 	
 	features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+	features.requireFeature([](vk::PhysicalDeviceFeatures& features) {
+		features.setGeometryShader(true);
+	});
 	
 	vkcv::Core core = vkcv::Core::create(
 		"Firework",
@@ -318,7 +322,8 @@ int main(int argc, const char **argv) {
 		{ smokeDescriptorLayout }
 	});
 	
-	auto swapchainExtent = core.getSwapchain(windowHandle).getExtent();
+	auto swapchainHandle = core.getWindow(windowHandle).getSwapchain();
+	auto swapchainExtent = core.getSwapchainExtent(swapchainHandle);
 	
 	const vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat;
 	
@@ -361,18 +366,21 @@ int main(int argc, const char **argv) {
 	particles.resize(PARTICLE_COUNT);
 	InitializeParticles(particles);
 	
-	vkcv::Buffer<particle_t> particleBuffer = core.createBuffer<particle_t>(
-		vkcv::BufferType::STORAGE,
-		particles.size(),
-		vkcv::BufferMemoryType::DEVICE_LOCAL,
-		false,
-		true
+	auto particleBuffer = vkcv::buffer<particle_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			particles.size(),
+			vkcv::BufferMemoryType::DEVICE_LOCAL,
+			true
 	);
 	
 	particleBuffer.fill(particles);
 	
-	vkcv::Buffer<particle_t> particleBufferCopy =
-		core.createBuffer<particle_t>(vkcv::BufferType::STORAGE, particles.size());
+	auto particleBufferCopy = vkcv::buffer<particle_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			particles.size()
+	);
 
 	particleBufferCopy.fill(particles);
 	
@@ -392,9 +400,10 @@ int main(int argc, const char **argv) {
 		);
 	}
 	
-	vkcv::Buffer<float> randomBuffer = core.createBuffer<float>(
-		vkcv::BufferType::STORAGE,
-		randomData.size()
+	auto randomBuffer = vkcv::buffer<float>(
+			core,
+			vkcv::BufferType::STORAGE,
+			randomData.size()
 	);
 	
 	randomBuffer.fill(randomData);
@@ -402,9 +411,10 @@ int main(int argc, const char **argv) {
 	std::vector<event_t> events;
 	InitializeFireworkEvents(events);
 	
-	vkcv::Buffer<event_t> eventBuffer = core.createBuffer<event_t>(
-		vkcv::BufferType::STORAGE,
-		events.size()
+	auto eventBuffer = vkcv::buffer<event_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			events.size()
 	);
 	
 	eventBuffer.fill(events);
@@ -416,8 +426,11 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(generationDescriptorSet, writes);
 	}
 
-	vkcv::Buffer<uint32_t> startIndexBuffer =
-		core.createBuffer<uint32_t>(vkcv::BufferType::STORAGE, eventBuffer.getCount());
+	auto startIndexBuffer = vkcv::buffer<uint32_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			eventBuffer.getCount()
+	);
 
 	{
 		vkcv::DescriptorWrites writes;
@@ -441,15 +454,19 @@ int main(int argc, const char **argv) {
 		smokes.push_back(smoke);
 	}
 	
-	vkcv::Buffer<smoke_t> smokeBuffer = core.createBuffer<smoke_t>(
-		vkcv::BufferType::STORAGE,
-		smokes.size()
+	auto smokeBuffer = vkcv::buffer<smoke_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			smokes.size()
 	);
 	
 	smokeBuffer.fill(smokes);
 	
-	vkcv::Buffer<uint32_t> smokeIndexBuffer = core.createBuffer<uint32_t>(
-		vkcv::BufferType::STORAGE, 3, vkcv::BufferMemoryType::HOST_VISIBLE
+	auto smokeIndexBuffer = vkcv::buffer<uint32_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			3,
+			vkcv::BufferMemoryType::HOST_VISIBLE
 	);
 	
 	uint32_t* smokeIndices = smokeIndexBuffer.map();
@@ -478,9 +495,10 @@ int main(int argc, const char **argv) {
 		trails.push_back(trail);
 	}
 	
-	vkcv::Buffer<trail_t> trailBuffer = core.createBuffer<trail_t>(
-		vkcv::BufferType::STORAGE,
-		trails.size()
+	auto trailBuffer = vkcv::buffer<trail_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			trails.size()
 	);
 	
 	trailBuffer.fill(trails);
@@ -499,9 +517,10 @@ int main(int argc, const char **argv) {
 		points.push_back(point);
 	}
 	
-	vkcv::Buffer<point_t> pointBuffer = core.createBuffer<point_t>(
-		vkcv::BufferType::STORAGE,
-		points.size()
+	auto pointBuffer = vkcv::buffer<point_t>(
+			core,
+			vkcv::BufferType::STORAGE,
+			points.size()
 	);
 	
 	pointBuffer.fill(points);
@@ -513,7 +532,12 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(trailDescriptorSet, writes);
 	}
 	
-	vkcv::Buffer<glm::vec3> cubePositions = core.createBuffer<glm::vec3>(vkcv::BufferType::VERTEX, 8);
+	auto cubePositions = vkcv::buffer<glm::vec3>(
+			core,
+			vkcv::BufferType::VERTEX,
+			8
+	);
+	
 	cubePositions.fill({
 		glm::vec3(-1.0f, -1.0f, -1.0f),
 		glm::vec3(+1.0f, -1.0f, -1.0f),
@@ -525,7 +549,12 @@ int main(int argc, const char **argv) {
 		glm::vec3(+1.0f, +1.0f, +1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> cubeIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 36);
+	auto cubeIndices = vkcv::buffer<uint16_t>(
+			core,
+			vkcv::BufferType::INDEX,
+			36
+	);
+	
 	cubeIndices.fill({
 		0, 2, 3,
 		0, 3, 1,
@@ -569,17 +598,14 @@ int main(int argc, const char **argv) {
 		vkcv::Multisampling::None
 	));
 	
-	vkcv::GraphicsPipelineConfig smokePipelineDefinition{
+	vkcv::GraphicsPipelineConfig smokePipelineDefinition (
 		smokeShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{smokeLayout},
-		{smokeDescriptorLayout, generationDescriptorLayout},
-		true
-	};
+		{smokeDescriptorLayout, generationDescriptorLayout}
+	);
 	
-	smokePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	smokePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle smokePipeline = core.createGraphicsPipeline(smokePipelineDefinition);
 	
@@ -592,18 +618,15 @@ int main(int argc, const char **argv) {
 	
 	const vkcv::VertexLayout trailLayout { vbTrail };
 	
-	vkcv::GraphicsPipelineConfig trailPipelineDefinition{
+	vkcv::GraphicsPipelineConfig trailPipelineDefinition (
 		trailShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{trailLayout},
-		{trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout},
-		true
-	};
+		{trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout}
+	);
 	
-	trailPipelineDefinition.m_PrimitiveTopology = vkcv::PrimitiveTopology::PointList;
-	trailPipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	trailPipelineDefinition.setPrimitiveTopology(vkcv::PrimitiveTopology::PointList);
+	trailPipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle trailPipeline = core.createGraphicsPipeline(trailPipelineDefinition);
 	
@@ -618,14 +641,24 @@ int main(int argc, const char **argv) {
 		smokeBuffer.getCount()
 	));
 	
-	vkcv::Buffer<glm::vec2> trianglePositions = core.createBuffer<glm::vec2>(vkcv::BufferType::VERTEX, 3);
+	auto trianglePositions = vkcv::buffer<glm::vec2>(
+			core,
+			vkcv::BufferType::VERTEX,
+			3
+	);
+	
 	trianglePositions.fill({
 		glm::vec2(-1.0f, -1.0f),
 		glm::vec2(+0.0f, +1.5f),
 		glm::vec2(+1.0f, -1.0f)
 	});
 	
-	vkcv::Buffer<uint16_t> triangleIndices = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, 3);
+	auto triangleIndices = vkcv::buffer<uint16_t>(
+			core,
+			vkcv::BufferType::INDEX,
+			3
+	);
+	
 	triangleIndices.fill({
 		0, 1, 2
 	});
@@ -663,17 +696,14 @@ int main(int argc, const char **argv) {
 
 	const vkcv::VertexLayout particleLayout { vbParticles };
 
-	vkcv::GraphicsPipelineConfig particlePipelineDefinition{
+	vkcv::GraphicsPipelineConfig particlePipelineDefinition (
 		particleShaderProgram,
-		UINT32_MAX,
-		UINT32_MAX,
 		renderPass,
 		{particleLayout},
-		{descriptorSetLayout},
-		true
-	};
+		{descriptorSetLayout}
+	);
 	
-	particlePipelineDefinition.m_blendMode = vkcv::BlendMode::Additive;
+	particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive);
 	
 	vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition);
 	
@@ -963,10 +993,10 @@ int main(int argc, const char **argv) {
 		
 		auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
 		
-		uint32_t voxelDispatchCount[3];
-		voxelDispatchCount[0] = std::ceil(voxelWidth / 4.f);
-		voxelDispatchCount[1] = std::ceil(voxelHeight / 4.f);
-		voxelDispatchCount[2] = std::ceil(voxelDepth / 4.f);
+		auto voxelDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(voxelWidth, voxelHeight, voxelDepth),
+				vkcv::DispatchSize(4, 4, 4)
+		);
 		
 		core.recordBeginDebugLabel(cmdStream, "Voxel clear", { 0.5f, 0.25f, 0.8f, 1.0f });
 		core.prepareImageForStorage(cmdStream, voxelRed.getHandle());
@@ -990,10 +1020,7 @@ int main(int argc, const char **argv) {
 		core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle());
 		
-		uint32_t particleDispatchCount[3];
-		particleDispatchCount[0] = std::ceil(particleBuffer.getCount() / 256.f);
-		particleDispatchCount[1] = 1;
-		particleDispatchCount[2] = 1;
+		auto particleDispatchCount = vkcv::dispatchInvocations(particleBuffer.getCount(), 256);
 		
 		vkcv::PushConstants pushConstantsTime (2 * sizeof(float));
 		pushConstantsTime.appendDrawcall(time_values);
@@ -1015,10 +1042,7 @@ int main(int argc, const char **argv) {
 		
 		core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle());
 		
-		uint32_t smokeDispatchCount[3];
-		smokeDispatchCount[0] = std::ceil(smokeBuffer.getCount() / 256.f);
-		smokeDispatchCount[1] = 1;
-		smokeDispatchCount[2] = 1;
+		auto smokeDispatchCount = vkcv::dispatchInvocations(smokeBuffer.getCount(), 256);
 		
 		core.recordBeginDebugLabel(cmdStream, "Smoke scaling", { 0.0f, 0.0f, 1.0f, 1.0f });
 		core.recordComputeDispatchToCmdStream(
@@ -1046,10 +1070,7 @@ int main(int argc, const char **argv) {
 		core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle());
 		core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle());
 		
-		uint32_t trailDispatchCount[3];
-		trailDispatchCount[0] = std::ceil(trailBuffer.getCount() / 256.f);
-		trailDispatchCount[1] = 1;
-		trailDispatchCount[2] = 1;
+		auto trailDispatchCount = vkcv::dispatchInvocations(trailBuffer.getCount(), 256);
 		
 		core.recordBeginDebugLabel(cmdStream, "Trail update", { 0.0f, 0.0f, 1.0f, 1.0f });
 		core.recordComputeDispatchToCmdStream(
@@ -1082,7 +1103,6 @@ int main(int argc, const char **argv) {
 		core.recordBeginDebugLabel(cmdStream, "Draw particles", { 1.0f, 0.0f, 1.0f, 1.0f });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			particlePipeline,
 			pushConstantsDraw0,
 			{ drawcallsParticles },
@@ -1125,7 +1145,6 @@ int main(int argc, const char **argv) {
 		
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			smokePipeline,
 			pushConstantsDraw1,
 			{ drawcallsSmokes },
@@ -1158,7 +1177,6 @@ int main(int argc, const char **argv) {
 		core.recordBeginDebugLabel(cmdStream, "Draw trails", { 0.75f, 0.5f, 1.0f, 1.0f });
 		core.recordDrawcallsToCmdStream(
 			cmdStream,
-			renderPass,
 			trailPipeline,
 			pushConstantsDraw1,
 			{ drawcallsTrails },
@@ -1229,10 +1247,10 @@ int main(int argc, const char **argv) {
 		core.prepareImageForStorage(cmdStream, voxelData[0]);
 		core.prepareImageForStorage(cmdStream, voxelSamples.getHandle());
 		
-		uint32_t sampleDispatchCount[3];
-		sampleDispatchCount[0] = std::ceil(voxelWidth / 8.f);
-		sampleDispatchCount[1] = std::ceil(voxelHeight / 8.f);
-		sampleDispatchCount[2] = 1;
+		auto sampleDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(voxelWidth, voxelHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 		
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
@@ -1261,10 +1279,10 @@ int main(int argc, const char **argv) {
 		core.writeDescriptorSet(addDescriptor, addDescriptorWrites);
 		core.prepareImageForSampling(cmdStream, voxelSamples.getHandle());
 		
-		uint32_t colorDispatchCount[3];
-		colorDispatchCount[0] = std::ceil(swapchainWidth / 8.f);
-		colorDispatchCount[1] = std::ceil(swapchainHeight / 8.f);
-		colorDispatchCount[2] = 1;
+		auto colorDispatchCount = vkcv::dispatchInvocations(
+				vkcv::DispatchSize(swapchainWidth, swapchainHeight),
+				vkcv::DispatchSize(8, 8)
+		);
 		
 		core.recordComputeDispatchToCmdStream(
 			cmdStream,
@@ -1310,11 +1328,15 @@ int main(int argc, const char **argv) {
 		gui.beginGUI();
 		ImGui::Begin("Settings");
 		
-		bool listbox = ImGui::BeginListBox(" ");
-		bool firework = ImGui::Selectable("Firework");
-		bool sparkler = ImGui::Selectable("Sparkler");
-		bool nested = ImGui::Selectable("Nested Firework");
-		ImGui::EndListBox();
+		bool firework, sparkler, nested;
+		if (ImGui::BeginListBox(" ")) {
+			firework = ImGui::Selectable("Firework");
+			sparkler = ImGui::Selectable("Sparkler");
+			nested = ImGui::Selectable("Nested Firework");
+			
+			ImGui::EndListBox();
+		}
+		
 		bool resetTime = ImGui::Button("Reset");
 		auto color = glm::vec3(0.0f);