diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index 542bec6b20a7bb5d52fb5c7e2511e9f0dc6f3de5..46f617bc6f7691dd5227c8a472634a08679ed385 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -1,11 +1,12 @@
-
 #include <iostream>
 #include <vkcv/Context.hpp>
 
 int main(int argc, const char** argv) {
 	vkcv::Context context = vkcv::Context::create(
-			"First Triangle",
-			VK_MAKE_VERSION(0, 0, 1)
+		"First Triangle",
+		VK_MAKE_VERSION(0, 0, 1),
+		20,
+		{vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eTransfer}
 	);
 
 	const vk::Instance& instance = context.getInstance();
diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp
index a1c54c0804318ca946a793f5abf10e4b15a2030b..ac538984b0ebbce7462fb0c2c5dec52ba991f64f 100644
--- a/src/vkcv/Context.cpp
+++ b/src/vkcv/Context.cpp
@@ -1,6 +1,11 @@
-
 #include "Context.hpp"
 
+
+std::vector<const char*> validationLayers = {
+	"VK_LAYER_KHRONOS_validation"
+};
+
+
 namespace vkcv {
 
 	Context::Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device)
@@ -11,8 +16,35 @@ namespace vkcv {
 		m_device.destroy();
 		m_instance.destroy();
 	}
-	
-	Context Context::create(const char* applicationName, uint32_t applicationVersion) {
+
+	Context Context::create(const char* applicationName, uint32_t applicationVersion, uint32_t queueCount, std::vector<vk::QueueFlagBits> queueFlags, std::vector<const char*> instanceExtensions, std::vector<const char*> deviceExtensions) {
+		glfwInit();
+		
+		// check for layer support
+		uint32_t layerCount = 0;
+		vk::enumerateInstanceLayerProperties(&layerCount, nullptr);
+		std::vector<vk::LayerProperties> layerProperties(layerCount);
+		vk::enumerateInstanceLayerProperties(&layerCount, layerProperties.data());
+		std::vector<const char*> supportedLayers;
+		for (auto& elem : layerProperties)
+			supportedLayers.push_back(elem.layerName);
+
+		// if in debug mode, check if validation layers are supported. Enable them if supported
+		if (enableValidationLayers && !Context::checkSupport(supportedLayers, validationLayers))
+			throw std::runtime_error("Validation layers requested but not available!");
+		
+		// check for extension support
+		std::vector<vk::ExtensionProperties> instanceExtensionProperties = vk::enumerateInstanceExtensionProperties();
+		std::vector<const char*> supportedExtensions;
+		for (auto& elem : instanceExtensionProperties)
+			supportedExtensions.push_back(elem.extensionName);
+		if (!checkSupport(supportedExtensions, instanceExtensions))
+			throw std::runtime_error("The requested instance extensions are not supported!");
+
+		// for GLFW: get all required extensions
+		std::vector<const char*> requiredExtensions = Context::getRequiredExtensions();
+		instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end());
+
 		const vk::ApplicationInfo applicationInfo (
 				applicationName,
 				applicationVersion,
@@ -20,35 +52,45 @@ namespace vkcv {
 				VK_MAKE_VERSION(0, 0, 1),
 				VK_HEADER_VERSION_COMPLETE
 		);
-		
-		// TODO: enable validation layers in debug build and add required extensions
-		const vk::InstanceCreateInfo instanceCreateInfo (
-				vk::InstanceCreateFlags(),
-				&applicationInfo,
-				0,
-				nullptr,
-				0,
-				nullptr
+
+		const vk::InstanceCreateInfo instanceCreateInfo(
+			vk::InstanceCreateFlags(),
+			&applicationInfo,
+			(enableValidationLayers) ? static_cast<uint32_t>(validationLayers.size()) : 0,
+			(enableValidationLayers) ? validationLayers.data() : nullptr,
+			static_cast<uint32_t>(instanceExtensions.size()),
+			instanceExtensions.data()
 		);
-		
+
 		vk::Instance instance = vk::createInstance(instanceCreateInfo);
-		
-		// TODO: search for the best physical device (discrete GPU)
-		vk::PhysicalDevice physicalDevice = instance.enumeratePhysicalDevices()[0];
-		
-		// TODO: create required queues, add validation layers and required extensions
+
+		std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices();
+		vk::PhysicalDevice physicalDevice = pickPhysicalDevice(instance);
+
+		// check for physical device extension support
+		std::vector<vk::ExtensionProperties> deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties();
+		supportedExtensions.clear();
+		for (auto& elem : deviceExtensionProperties)
+			supportedExtensions.push_back(elem.extensionName);
+		if (!checkSupport(supportedExtensions, deviceExtensions))
+			throw std::runtime_error("The requested device extensions are not supported by the physical device!");
+
+		// create required queues
+		std::vector<vk::DeviceQueueCreateInfo> qCreateInfos = getQueueCreateInfos(physicalDevice, queueCount, queueFlags);
+
 		const vk::DeviceCreateInfo deviceCreateInfo (
 				vk::DeviceCreateFlags(),
-				0,
-				nullptr,
-				0,
-				nullptr,
-				0,
-				nullptr,
-				nullptr
+				qCreateInfos.size(),
+				qCreateInfos.data(),
+				(enableValidationLayers) ? static_cast<uint32_t>(validationLayers.size()) : 0,
+				(enableValidationLayers) ? validationLayers.data() : nullptr,
+				deviceExtensions.size(),
+				deviceExtensions.data(),
+				nullptr		// Should our device use some features??? If yes: TODO
 		);
 		
 		vk::Device device = physicalDevice.createDevice(deviceCreateInfo);
+		// TODO: implement device.getQueue() to access the queues, if needed
 		
 		return Context(instance, physicalDevice, device);
 	}
@@ -64,4 +106,171 @@ namespace vkcv {
 	const vk::Device& Context::getDevice() const {
 		return m_device;
 	}
+
+	/// <summary>
+	/// All existing physical devices will be evaluated by 
+	/// </summary>
+	/// <param name="instance">The instance.</param>
+	/// <returns>The optimal physical device.</returns>
+	/// <seealso cref="Context.deviceScore">
+	vk::PhysicalDevice Context::pickPhysicalDevice(vk::Instance& instance) {
+		vk::PhysicalDevice phyDevice;
+		uint32_t deviceCount = 0;
+		instance.enumeratePhysicalDevices(&deviceCount, nullptr);
+		if (deviceCount == 0) {
+			throw std::runtime_error("failed to find GPUs with Vulkan support!");
+		}
+		std::vector<vk::PhysicalDevice> devices(deviceCount);
+		instance.enumeratePhysicalDevices(&deviceCount, devices.data());
+		int max_score = -1;
+		for (const auto& device : devices) {
+			int score = deviceScore(device);
+			if (score > max_score) {
+				max_score = score;
+				phyDevice = device;
+			}
+		}
+
+		if (&phyDevice == nullptr) {
+			throw std::runtime_error("failed to find a suitable GPU!");
+		}
+
+		return phyDevice;
+	}
+
+	/// <summary>
+	/// The physical device is evaluated by three categories: discrete GPU vs. integrated GPU, amount of queues and
+	/// its abilities, and VRAM.
+	/// </summary>
+	/// <param name="physicalDevice"> The physical device. </param>
+	/// <returns></returns>
+	int Context::deviceScore(const vk::PhysicalDevice& physicalDevice) {
+		int score = 0;
+		vk::PhysicalDeviceProperties properties = physicalDevice.getProperties();
+		std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+
+		// for every queue family compute queue flag bits and the amount of queues
+		for (const auto& qFamily : qFamilyProperties) {
+			uint32_t qCount = qFamily.queueCount;
+			uint32_t bitCount = (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0)
+				+ (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eSparseBinding) != 0);
+			score += qCount * bitCount;
+		}
+
+		// compute the VRAM of the physical device
+		vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties();
+		int vram = static_cast<int>(memoryProperties.memoryHeaps[0].size / 1E9);
+		score *= vram;
+
+		if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
+			// nice!
+			score *= 2;
+		}
+		else if (properties.deviceType == vk::PhysicalDeviceType::eIntegratedGpu) {
+			// not perfect but ok
+		}
+		else {
+			// not so nice
+			score *= -1;
+		}
+
+		return score;
+	}
+
+	/// <summary>
+	/// Creates a candidate list of queues that all meet the desired flags and then creates the maximum possible number
+	/// of queues. If the number of desired queues is not sufficient, the remaining queues are created from the next
+	/// candidate from the list.
+	/// </summary>
+	/// <param name="physicalDevice">The physical device</param>
+	/// <param name="queueCount">The amount of queues to be created</param>
+	/// <param name="queueFlags">The abilities which have to be supported by any created queue</param>
+	/// <returns></returns>
+	std::vector<vk::DeviceQueueCreateInfo> Context::getQueueCreateInfos(vk::PhysicalDevice& physicalDevice, uint32_t queueCount, std::vector<vk::QueueFlagBits>& queueFlags) {
+		std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
+		std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties();
+		std::vector<vk::QueueFamilyProperties> qFamilyCandidates;
+
+		// search for queue families which support the desired queue flag bits
+		for (auto& qFamily : qFamilyProperties) {
+			bool supported = true;
+			for (auto qFlag : queueFlags) {
+				supported = supported && (static_cast<uint32_t>(qFlag & qFamily.queueFlags) != 0);
+			}
+			if (supported) {
+				qFamilyCandidates.push_back(qFamily);
+			}
+		}
+
+		uint32_t create = queueCount;
+		for (int i = 0; i < qFamilyCandidates.size() && create > 0; i++) {
+			const int availableQueues = qFamilyCandidates[i].queueCount;
+			if (create >= availableQueues) {
+				float* qPriorities = new float[availableQueues];
+				std::fill_n(qPriorities, availableQueues, 1.f);		// all queues have the same priorities
+				vk::DeviceQueueCreateInfo qCreateInfo(
+					vk::DeviceQueueCreateFlags(),
+					i,
+					qFamilyCandidates[i].queueCount,
+					qPriorities
+				);
+				queueCreateInfos.push_back(qCreateInfo);
+				create -= qFamilyCandidates[i].queueCount;
+			}
+			else {
+				float* qPriorities = new float[create];
+				std::fill_n(qPriorities, create, 1.f);				// all queues have the same priorities
+				vk::DeviceQueueCreateInfo qCreateInfo(
+					vk::DeviceQueueCreateFlags(),
+					i,
+					create,
+					qPriorities
+				);
+				queueCreateInfos.push_back(qCreateInfo);
+				create -= create;
+			}
+		}
+
+		return queueCreateInfos;
+	}
+
+	/// <summary>
+	/// With the help of the reference <paramref name="supported"> all elements in <paramref name="check"/> checked,
+	/// if they are supported by the physical device.
+	/// </summary>
+	/// <param name="supported">The reference that can be used to check <paramref name="check"/></param>
+	/// <param name="check">The elements to be checked</param>
+	/// <returns>True, if all elements in <param name="check"> are supported</returns>
+	bool Context::checkSupport(std::vector<const char*>& supported, std::vector<const char*>& check) {
+		for (auto checkElem : check) {
+			bool found = false;
+			for (auto supportedElem : supported) {
+				if (strcmp(supportedElem, checkElem) == 0) {
+					found = true;
+					break;
+				}
+			}
+			if (!found)
+				return false;
+		}
+		return true;
+	}
+
+	/// <summary>
+	/// Gets all extensions required, i.e. GLFW and advanced debug extensions.
+	/// </summary>
+	/// <returns>The required extensions</returns>
+	std::vector<const char*> Context::getRequiredExtensions() {
+		uint32_t glfwExtensionCount = 0;
+		const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
+		std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
+
+		if (enableValidationLayers) {
+			extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+		}
+
+		return extensions;
+	}
 }
diff --git a/src/vkcv/Context.hpp b/src/vkcv/Context.hpp
index c6b365a44eee26d4691c677f5e0525e6b5b13c2a..e9b0f62446e2146982d20047f812ab52e6695182 100644
--- a/src/vkcv/Context.hpp
+++ b/src/vkcv/Context.hpp
@@ -1,6 +1,13 @@
 #pragma once
-
 #include <vulkan/vulkan.hpp>
+#include <GLFW/glfw3.h>
+#include <iostream>
+
+#ifdef NDEBUG
+const bool enableValidationLayers = false;
+#else
+const bool enableValidationLayers = true;
+#endif
 
 namespace vkcv {
 
@@ -9,9 +16,10 @@ namespace vkcv {
 		vk::Instance m_instance;
 		vk::PhysicalDevice m_physicalDevice;
 		vk::Device m_device;
-		
+
 		Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device);
 
+
 	public:
 		Context(const Context &other) = delete;
 		Context(Context &&other) = default;
@@ -29,9 +37,13 @@ namespace vkcv {
 
 		Context& operator=(const Context &other) = delete;
 		Context& operator=(Context &&other) = default;
-		
-		static Context create(const char* applicationName, uint32_t applicationVersion);
-		
+
+		static Context create(const char* applicationName, uint32_t applicationVersion, uint32_t queueCount = 1, const std::vector<vk::QueueFlagBits> queueFlags = {}, std::vector<const char*> instanceExtensions = {}, std::vector<const char*> deviceExtensions = {});
+		static bool checkSupport(std::vector<const char*> &supported, std::vector<const char*> &check);
+		static std::vector<const char*> getRequiredExtensions();
+		static vk::PhysicalDevice Context::pickPhysicalDevice(vk::Instance& instance);
+		static int deviceScore(const vk::PhysicalDevice &physicalDevice);
+		static std::vector<vk::DeviceQueueCreateInfo> getQueueCreateInfos(vk::PhysicalDevice& physicalDevice, uint32_t queueCount, std::vector<vk::QueueFlagBits> &queueFlags);
 	};
 
 }