Jelajahi Sumber

Improve shader support

Daniele Bartolini 10 tahun lalu
induk
melakukan
732f35ec92

+ 9 - 3
src/device.cpp

@@ -6,7 +6,6 @@
 #include "device.h"
 #include "array.h"
 #include "config.h"
-#include "debug_line.h"
 #include "input_manager.h"
 #include "log.h"
 #include "lua_environment.h"
@@ -63,6 +62,7 @@ Device::Device(const DeviceOptions& opts)
 	, _resource_loader(NULL)
 	, _resource_manager(NULL)
 	, _input_manager(NULL)
+	, _shader_manager(NULL)
 	, _worlds(default_allocator())
 	, _bgfx_allocator(default_allocator())
 {
@@ -95,8 +95,9 @@ void Device::init()
 		, &_bgfx_allocator
 		);
 
+	_shader_manager = CE_NEW(_allocator, ShaderManager)(default_allocator());
+
 	material_manager::init();
-	debug_line::init();
 
 	audio_globals::init();
 	physics_globals::init();
@@ -133,9 +134,9 @@ void Device::shutdown()
 	physics_globals::shutdown();
 	audio_globals::shutdown();
 
-	debug_line::shutdown();
 	material_manager::shutdown();
 
+	CE_DELETE(_allocator, _shader_manager);
 	CE_DELETE(_allocator, _input_manager);
 	CE_DELETE(_allocator, _resource_manager);
 	CE_DELETE(_allocator, _resource_loader);
@@ -295,6 +296,11 @@ InputManager* Device::input_manager()
 	return _input_manager;
 }
 
+ShaderManager* Device::shader_manager()
+{
+	return _shader_manager;
+}
+
 bool Device::process_events()
 {
 	OsEvent event;

+ 5 - 0
src/device.h

@@ -21,6 +21,7 @@
 #include "log.h"
 #include "proxy_allocator.h"
 #include "string_utils.h"
+#include "shader_manager.h"
 #include <bx/allocator.h>
 #include <bgfx/bgfx.h>
 
@@ -109,6 +110,9 @@ struct Device
 	/// Returns the input manager.
 	InputManager* input_manager();
 
+	/// Returns the shader manager.
+	ShaderManager* shader_manager();
+
 private:
 
 	bool process_events();
@@ -146,6 +150,7 @@ private:
 	ResourceLoader* _resource_loader;
 	ResourceManager* _resource_manager;
 	InputManager* _input_manager;
+	ShaderManager* _shader_manager;
 
 	Array<World*> _worlds;
 

+ 12 - 118
src/renderers/debug_line.cpp

@@ -9,125 +9,20 @@
 #include "vector3.h"
 #include "matrix4x4.h"
 #include "error.h"
+#include "device.h"
 #include <string.h> // memcpy
-#include <bgfx/bgfx.h>
 
 namespace crown
 {
 
-namespace debug_line
-{
-#if CROWN_PLATFORM_LINUX || CROWN_PLATFORM_ANDROID
-	static const uint8_t vs_h[325] =
-	{
-		0x56, 0x53, 0x48, 0x04, 0xa4, 0x8b, 0xef, 0x49, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH....I...u_mod
-		0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x01, 0x00, 0x00, 0x01, 0x00, // elViewProj......
-		0x20, 0x01, 0x00, 0x00, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, //  ...attribute hi
-		0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, // ghp vec4 a_color
-		0x30, 0x3b, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x68, 0x69, 0x67, // 0;.attribute hig
-		0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x61, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, // hp vec3 a_positi
-		0x6f, 0x6e, 0x3b, 0x0a, 0x76, 0x61, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, // on;.varying high
-		0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, // p vec4 v_color0;
-		0x0a, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, // .uniform highp m
-		0x61, 0x74, 0x34, 0x20, 0x75, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, // at4 u_modelViewP
-		0x72, 0x6f, 0x6a, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, // roj;.void main (
-		0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, // ).{.  highp vec4
-		0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x3b, 0x0a, 0x20, 0x20, 0x74, 0x6d, 0x70, //  tmpvar_1;.  tmp
-		0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x3b, 0x0a, 0x20, // var_1.w = 1.0;.
-		0x20, 0x74, 0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x3d, 0x20, //  tmpvar_1.xyz =
-		0x61, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x67, 0x6c, // a_position;.  gl
-		0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x28, 0x75, 0x5f, 0x6d, // _Position = (u_m
-		0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x20, 0x2a, 0x20, 0x74, // odelViewProj * t
-		0x6d, 0x70, 0x76, 0x61, 0x72, 0x5f, 0x31, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x76, 0x5f, 0x63, 0x6f, // mpvar_1);.  v_co
-		0x6c, 0x6f, 0x72, 0x30, 0x20, 0x3d, 0x20, 0x61, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, // lor0 = a_color0;
-		0x0a, 0x7d, 0x0a, 0x0a, 0x00,                                                                   // .}...
-	};
-
-	static const uint8_t fs_h[89] =
-	{
-		0x46, 0x53, 0x48, 0x04, 0xa4, 0x8b, 0xef, 0x49, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x76, 0x61, // FSH....I..J...va
-		0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, // rying highp vec4
-		0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, //  v_color0;.void
-		0x6d, 0x61, 0x69, 0x6e, 0x20, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x67, 0x6c, 0x5f, 0x46, // main ().{.  gl_F
-		0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x76, 0x5f, 0x63, 0x6f, 0x6c, // ragColor = v_col
-		0x6f, 0x72, 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x00,                                           // or0;.}...
-	};
-#elif CROWN_PLATFORM_WINDOWS
-	static const uint8_t vs_h[419] =
-	{
-		0x56, 0x53, 0x48, 0x04, 0xa4, 0x8b, 0xef, 0x49, 0x01, 0x00, 0x0f, 0x75, 0x5f, 0x6d, 0x6f, 0x64, // VSH....I...u_mod
-		0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x04, 0x01, 0x00, 0x00, 0x04, 0x00, // elViewProj......
-		0x80, 0x01, 0x00, 0x03, 0xfe, 0xff, 0xfe, 0xff, 0x23, 0x00, 0x43, 0x54, 0x41, 0x42, 0x1c, 0x00, // ........#.CTAB..
-		0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, // ..W.............
-		0x00, 0x00, 0x04, 0x11, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, // ......P...0.....
-		0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5f, // [email protected]_
-		0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x50, 0x72, 0x6f, 0x6a, 0x00, 0x03, 0x00, // modelViewProj...
-		0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x73, // ..............vs
-		0x5f, 0x33, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, // _3_0.Microsoft (
-		0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, // R) HLSL Shader C
-		0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, 0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, // ompiler 9.29.952
-		0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0x51, 0x00, 0x00, 0x05, 0x04, 0x00, 0x0f, 0xa0, 0x00, 0x00, // .3111.Q.........
-		0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, // .?..............
-		0x00, 0x02, 0x0a, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, // ................
-		0x00, 0x80, 0x01, 0x00, 0x0f, 0x90, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, // ................
-		0x0f, 0xe0, 0x1f, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x80, 0x01, 0x00, 0x0f, 0xe0, 0x05, 0x00, // ................
-		0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x00, 0x90, 0x05, 0x00, // ................
-		0x00, 0x03, 0x01, 0x00, 0x0f, 0x80, 0x01, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0x55, 0x90, 0x02, 0x00, // ............U...
-		0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x05, 0x00, // ................
-		0x00, 0x03, 0x01, 0x00, 0x0f, 0x80, 0x02, 0x00, 0xe4, 0xa0, 0x01, 0x00, 0xaa, 0x90, 0x02, 0x00, // ................
-		0x00, 0x03, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x01, 0x00, // ................
-		0x00, 0x02, 0x01, 0x00, 0x0f, 0x80, 0x03, 0x00, 0xe4, 0xa0, 0x05, 0x00, 0x00, 0x03, 0x01, 0x00, // ................
-		0x0f, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x04, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, // ................
-		0x0f, 0x80, 0x00, 0x00, 0xe4, 0x80, 0x01, 0x00, 0xe4, 0x80, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, // ................
-		0x0f, 0x80, 0x00, 0x00, 0xe4, 0x90, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, // ................
-		0xe4, 0x80, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x0f, 0xe0, 0x01, 0x00, 0xe4, 0x80, 0xff, 0xff, // ................
-		0x00, 0x00, 0x00,                                                                               // ...
-	};
-
-	static const uint8_t fs_h[137] =
-	{
-		0x46, 0x53, 0x48, 0x04, 0xa4, 0x8b, 0xef, 0x49, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x03, 0xff, 0xff, // FSH....I..|.....
-		0xfe, 0xff, 0x16, 0x00, 0x43, 0x54, 0x41, 0x42, 0x1c, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, // ....CTAB....#...
-		0x00, 0x03, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00, // ................
-		0x1c, 0x00, 0x00, 0x00, 0x70, 0x73, 0x5f, 0x33, 0x5f, 0x30, 0x00, 0x4d, 0x69, 0x63, 0x72, 0x6f, // ....ps_3_0.Micro
-		0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, // soft (R) HLSL Sh
-		0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x39, 0x2e, // ader Compiler 9.
-		0x32, 0x39, 0x2e, 0x39, 0x35, 0x32, 0x2e, 0x33, 0x31, 0x31, 0x31, 0x00, 0x1f, 0x00, 0x00, 0x02, // 29.952.3111.....
-		0x0a, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0f, 0x90, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x0f, 0x80, // ................
-		0x00, 0x00, 0xe4, 0x90, 0xff, 0xff, 0x00, 0x00, 0x00,                                           // .........
-	};
-#endif
-
-	static bgfx::VertexDecl s_decl;
-	static bgfx::ProgramHandle s_prog;
-
-	void init()
-	{
-		s_decl
-			.begin()
-			.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
-			.add(bgfx::Attrib::Color0,   4, bgfx::AttribType::Uint8, true)
-			.end();
-
-		bgfx::ShaderHandle vs = bgfx::createShader(bgfx::makeRef(vs_h, sizeof(vs_h)));
-		CE_ASSERT(bgfx::isValid(vs), "Failed to create vertex shader");
-		bgfx::ShaderHandle fs = bgfx::createShader(bgfx::makeRef(fs_h, sizeof(fs_h)));
-		CE_ASSERT(bgfx::isValid(fs), "Failed to create fragment shader");
-		s_prog = bgfx::createProgram(vs, fs, true);
-		CE_ASSERT(bgfx::isValid(s_prog), "Failed to create program");
-	}
-
-	void shutdown()
-	{
-		bgfx::destroyProgram(s_prog);
-	}
-
-} // namespace debug_line
-
 DebugLine::DebugLine(bool depth_test)
-	: _depth_test(depth_test)
+	: _shader(depth_test ? "debug_line" : "debug_line_noz")
 	, _num(0)
 {
+	_vertex_decl.begin()
+		.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
+		.add(bgfx::Attrib::Color0,   4, bgfx::AttribType::Uint8, true)
+		.end();
 }
 
 void DebugLine::add_line(const Vector3& start, const Vector3& end, const Color4& color)
@@ -246,19 +141,18 @@ void DebugLine::submit()
 	if (!_num)
 		return;
 
-	if (!checkAvailTransientVertexBuffer(_num * 2, debug_line::s_decl))
+	if (!checkAvailTransientVertexBuffer(_num * 2, _vertex_decl))
 		return;
 
 	bgfx::TransientVertexBuffer tvb;
-	bgfx::allocTransientVertexBuffer(&tvb, _num * 2, debug_line::s_decl);
+	bgfx::allocTransientVertexBuffer(&tvb, _num * 2, _vertex_decl);
 	memcpy(tvb.data, _lines, sizeof(Line) * _num);
 
-	bgfx::setState(BGFX_STATE_PT_LINES
-		| BGFX_STATE_RGB_WRITE
-		| (_depth_test ? (BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_DEPTH_WRITE) : 0)
-		| BGFX_STATE_CULL_CW);
+	const ShaderManager::ShaderData& sd = device()->shader_manager()->get(_shader);
+
 	bgfx::setVertexBuffer(&tvb, 0, _num * 2);
-	bgfx::submit(0, debug_line::s_prog);
+	bgfx::setState(sd.state);
+	bgfx::submit(0, sd.program);
 }
 
 } // namespace crown

+ 5 - 8
src/renderers/debug_line.h

@@ -8,17 +8,12 @@
 #include "types.h"
 #include "config.h"
 #include "math_types.h"
+#include "string_id.h"
+#include <bgfx/bgfx.h>
 
 namespace crown
 {
 
-namespace debug_line
-{
-	void init();
-
-	void shutdown();
-} // namespace debug_line
-
 /// Draws lines.
 ///
 /// @ingroup Graphics
@@ -62,7 +57,9 @@ private:
 		uint32_t c1;
 	};
 
-	bool _depth_test;
+	StringId32 _shader;
+	bgfx::VertexDecl _vertex_decl;
+
 	uint32_t _num;
 	Line _lines[CROWN_MAX_DEBUG_LINES];
 };

+ 3 - 2
src/renderers/material.cpp

@@ -62,8 +62,9 @@ void Material::bind() const
 		bgfx::setUniform(buh, (char*) uh + sizeof(uh->uniform_handle));
 	}
 
-	Shader* shader = (Shader*) device()->resource_manager()->get(SHADER_TYPE, material_resource::shader(resource));
-	bgfx::submit(0, shader->program);
+	const ShaderManager::ShaderData& sd = device()->shader_manager()->get(material_resource::shader(resource));
+	bgfx::setState(sd.state);
+	bgfx::submit(0, sd.program);
 }
 
 void Material::set_float(const char* name, float val)

+ 670 - 127
src/renderers/shader.cpp

@@ -7,16 +7,17 @@
 #include "config.h"
 #include "filesystem.h"
 #include "os.h"
-#include "reader_writer.h"
 #include "resource_manager.h"
 #include "compile_options.h"
 #include "temp_allocator.h"
 #include "string_stream.h"
 #include "sjson.h"
 #include "map.h"
+#include "shader_manager.h"
+#include "device.h"
 
 #if CROWN_DEBUG
-#	define SHADERC_NAME "./shaderc-debug-"
+#	define SHADERC_NAME "shaderc-debug-"
 #else
 #	define SHADERC_NAME "shaderc-development-"
 #endif // CROWN_DEBUG
@@ -26,9 +27,9 @@
 #	define SHADERC_BITS "64"
 #endif // CROWN_ARCH_32BIT
 #if CROWN_PLATFORM_LINUX
-#	define SHADERC_PATH SHADERC_NAME""SHADERC_BITS
+#	define SHADERC_PATH "./" SHADERC_NAME "" SHADERC_BITS
 #elif CROWN_PLATFORM_WINDOWS
-#	define SHADERC_PATH SHADERC_NAME""SHADERC_BITS".exe"
+#	define SHADERC_PATH SHADERC_NAME "" SHADERC_BITS ".exe"
 #else
 # 	define SHADERC_PATH ""
 #endif // CROWN_PLATFORM_LINUX
@@ -58,154 +59,696 @@ namespace shader_resource
 		return os::execute_process(SHADERC_PATH, c_str(args), output);
 	}
 
-	void compile(const char* path, CompileOptions& opts)
+	struct DepthTest
 	{
-		Buffer buf = opts.read(path);
-		TempAllocator4096 ta;
-		JsonObject object(ta);
-		sjson::parse(buf, object);
-
-		DynamicString vs_code2(ta);
-		DynamicString fs_code2(ta);
-		DynamicString varying_def(ta);
-		DynamicString common_code(ta);
-		DynamicString vs_in_out(ta);
-		DynamicString fs_in_out(ta);
-		sjson::parse_string(object["vs_code"], vs_code2);
-		sjson::parse_string(object["fs_code"], fs_code2);
-		sjson::parse_string(object["varying_def"], varying_def);
-		sjson::parse_string(object["common"], common_code);
-		sjson::parse_string(object["vs_in_out"], vs_in_out);
-		sjson::parse_string(object["fs_in_out"], fs_in_out);
-
-		DynamicString vs_code(default_allocator());
-		DynamicString fs_code(default_allocator());
-		vs_code += vs_in_out;
-		vs_code += common_code;
-		vs_code += vs_code2;
-		fs_code += fs_in_out;
-		fs_code += common_code;
-		fs_code += fs_code2;
-
-		DynamicString vs_code_path(default_allocator());
-		DynamicString fs_code_path(default_allocator());
-		DynamicString varying_def_path(default_allocator());
-		DynamicString tmpvs_path(default_allocator());
-		DynamicString tmpfs_path(default_allocator());
-
-		opts.get_absolute_path("vs_code.tmp", vs_code_path);
-		opts.get_absolute_path("fs_code.tmp", fs_code_path);
-		opts.get_absolute_path("varying.tmp", varying_def_path);
-		opts.get_absolute_path("tmpvs", tmpvs_path);
-		opts.get_absolute_path("tmpfs", tmpfs_path);
-
-		File* vs_file = opts._fs.open(vs_code_path.c_str(), FileOpenMode::WRITE);
-		vs_file->write(vs_code.c_str(), vs_code.length());
-		opts._fs.close(*vs_file);
-
-		File* fs_file = opts._fs.open(fs_code_path.c_str(), FileOpenMode::WRITE);
-		fs_file->write(fs_code.c_str(), fs_code.length());
-		opts._fs.close(*fs_file);
-
-		File* varying_file = opts._fs.open(varying_def_path.c_str(), FileOpenMode::WRITE);
-		varying_file->write(varying_def.c_str(), varying_def.length());
-		opts._fs.close(*varying_file);
-
-		StringStream output(ta);
-		using namespace string_stream;
+		enum Enum
+		{
+			LESS,
+			LEQUAL,
+			EQUAL,
+			GEQUAL,
+			GREATER,
+			NOTEQUAL,
+			NEVER,
+			ALWAYS,
 
-		int exitcode = run_external_compiler(vs_code_path.c_str()
-			, tmpvs_path.c_str()
-			, varying_def_path.c_str()
-			, "vertex"
-			, opts.platform()
-			, output
-			);
-		RESOURCE_COMPILER_ASSERT(exitcode == 0
-			, opts
-			, "Failed to compile vertex shader:\n%s"
-			, c_str(output)
-			);
-
-		array::clear(output);
-		exitcode = run_external_compiler(fs_code_path.c_str()
-			, tmpfs_path.c_str()
-			, varying_def_path.c_str()
-			, "fragment"
-			, opts.platform()
-			, output
-			);
-		if (exitcode)
-		{
-			opts.delete_file(tmpvs_path.c_str());
-			RESOURCE_COMPILER_ASSERT(false
-				, opts
-				, "Failed to compile fragment shader:\n%s"
-				, c_str(output)
-				);
+			COUNT
+		};
+	};
+
+	struct DepthTestInfo
+	{
+		const char* name;
+		DepthTest::Enum value;
+	};
+
+	static DepthTestInfo _depth_test_map[] =
+	{
+		{ "less",     DepthTest::LESS     },
+		{ "lequal",   DepthTest::LEQUAL   },
+		{ "equal",    DepthTest::EQUAL    },
+		{ "gequal",   DepthTest::GEQUAL   },
+		{ "greater",  DepthTest::GREATER  },
+		{ "notequal", DepthTest::NOTEQUAL },
+		{ "never",    DepthTest::NEVER    },
+		{ "always",   DepthTest::ALWAYS   }
+	};
+	CE_STATIC_ASSERT(CE_COUNTOF(_depth_test_map) == DepthTest::COUNT);
+
+	static DepthTest::Enum name_to_depth_test(const char* name)
+	{
+		for (uint32_t i = 0; i < CE_COUNTOF(_depth_test_map); ++i)
+		{
+			if (strcmp(name, _depth_test_map[i].name) == 0)
+				return _depth_test_map[i].value;
+		}
+
+		return DepthTest::COUNT;
+	}
+
+	struct BlendFunction
+	{
+		enum Enum
+		{
+			ZERO,
+			ONE,
+			SRC_COLOR,
+			INV_SRC_COLOR,
+			SRC_ALPHA,
+			INV_SRC_ALPHA,
+			DST_ALPHA,
+			INV_DST_ALPHA,
+			DST_COLOR,
+			INV_DST_COLOR,
+			SRC_ALPHA_SAT,
+			FACTOR,
+			INV_FACTOR,
+
+			COUNT
+		};
+	};
+
+	struct BlendFunctionInfo
+	{
+		const char* name;
+		BlendFunction::Enum value;
+	};
+
+	static BlendFunctionInfo _blend_function_map[] =
+	{
+		{ "zero",          BlendFunction::ZERO          },
+		{ "one",           BlendFunction::ONE           },
+		{ "src_color",     BlendFunction::SRC_COLOR     },
+		{ "inv_src_color", BlendFunction::INV_SRC_COLOR },
+		{ "src_alpha",     BlendFunction::SRC_ALPHA     },
+		{ "inv_src_alpha", BlendFunction::INV_SRC_ALPHA },
+		{ "dst_alpha",     BlendFunction::DST_ALPHA     },
+		{ "inv_dst_alpha", BlendFunction::INV_DST_ALPHA },
+		{ "dst_color",     BlendFunction::DST_COLOR     },
+		{ "inv_dst_color", BlendFunction::INV_DST_COLOR },
+		{ "src_alpha_sat", BlendFunction::SRC_ALPHA_SAT },
+		{ "factor",        BlendFunction::FACTOR        },
+		{ "inv_factor",    BlendFunction::INV_FACTOR    }
+	};
+	CE_STATIC_ASSERT(CE_COUNTOF(_blend_function_map) == BlendFunction::COUNT);
+
+	static BlendFunction::Enum name_to_blend_function(const char* name)
+	{
+		for (uint32_t i = 0; i < CE_COUNTOF(_blend_function_map); ++i)
+		{
+			if (strcmp(name, _blend_function_map[i].name) == 0)
+				return _blend_function_map[i].value;
 		}
 
-		Buffer tmpvs = opts.read(tmpvs_path.c_str());
-		Buffer tmpfs = opts.read(tmpfs_path.c_str());
+		return BlendFunction::COUNT;
+	}
 
-		opts.write(uint32_t(SHADER_VERSION));
-		opts.write(uint32_t(array::size(tmpvs)));
-		opts.write(array::begin(tmpvs), array::size(tmpvs));
-		opts.write(uint32_t(array::size(tmpfs)));
-		opts.write(array::begin(tmpfs), array::size(tmpfs));
+	struct BlendEquation
+	{
+		enum Enum
+		{
+			ADD,
+			SUB,
+			REVSUB,
+			MIN,
+			MAX,
+
+			COUNT
+		};
+	};
+
+	struct BlendEquationInfo
+	{
+		const char* name;
+		BlendEquation::Enum value;
+	};
 
-		opts.delete_file(vs_code_path.c_str());
-		opts.delete_file(fs_code_path.c_str());
-		opts.delete_file(varying_def_path.c_str());
-		opts.delete_file(tmpvs_path.c_str());
-		opts.delete_file(tmpfs_path.c_str());
+	static BlendEquationInfo _blend_equation_map[] =
+	{
+		{ "add",    BlendEquation::ADD    },
+		{ "sub",    BlendEquation::SUB    },
+		{ "revsub", BlendEquation::REVSUB },
+		{ "min",    BlendEquation::MIN    },
+		{ "max",    BlendEquation::MAX    }
+	};
+	CE_STATIC_ASSERT(CE_COUNTOF(_blend_equation_map) == BlendEquation::COUNT);
+
+	static BlendEquation::Enum name_to_blend_equation(const char* name)
+	{
+		for (uint32_t i = 0; i < CE_COUNTOF(_blend_equation_map); ++i)
+		{
+			if (strcmp(name, _blend_equation_map[i].name) == 0)
+				return _blend_equation_map[i].value;
+		}
+
+		return BlendEquation::COUNT;
 	}
 
-	void* load(File& file, Allocator& a)
+	struct CullMode
+	{
+		enum Enum
+		{
+			CW,
+			CCW,
+
+			COUNT
+		};
+	};
+
+	struct CullModeInfo
+	{
+		const char* name;
+		CullMode::Enum value;
+	};
+
+	static CullModeInfo _cull_mode_map[] =
+	{
+		{ "cw",  CullMode::CW  },
+		{ "ccw", CullMode::CCW }
+	};
+	CE_STATIC_ASSERT(CE_COUNTOF(_cull_mode_map) == CullMode::COUNT);
+
+	static CullMode::Enum name_to_cull_mode(const char* name)
+	{
+		for (uint32_t i = 0; i < CE_COUNTOF(_cull_mode_map); ++i)
+		{
+			if (strcmp(name, _cull_mode_map[i].name) == 0)
+				return _cull_mode_map[i].value;
+		}
+
+		return CullMode::COUNT;
+	}
+
+	struct PrimitiveType
+	{
+		enum Enum
+		{
+			PT_TRISTRIP,
+			PT_LINES,
+			PT_LINESTRIP,
+			PT_POINTS,
+
+			COUNT
+		};
+	};
+
+	struct PrimitiveTypeInfo
+	{
+		const char* name;
+		PrimitiveType::Enum value;
+	};
+
+	static PrimitiveTypeInfo _primitive_type_map[] =
+	{
+		{ "pt_tristrip",  PrimitiveType::PT_TRISTRIP  },
+		{ "pt_lines",     PrimitiveType::PT_LINES     },
+		{ "pt_linestrip", PrimitiveType::PT_LINESTRIP },
+		{ "pt_points",    PrimitiveType::PT_POINTS    }
+	};
+	CE_STATIC_ASSERT(CE_COUNTOF(_primitive_type_map) == PrimitiveType::COUNT);
+
+	static PrimitiveType::Enum name_to_primitive_type(const char* name)
+	{
+		for (uint32_t i = 0; i < CE_COUNTOF(_primitive_type_map); ++i)
+		{
+			if (strcmp(name, _primitive_type_map[i].name) == 0)
+				return _primitive_type_map[i].value;
+		}
+
+		return PrimitiveType::COUNT;
+	}
+
+	struct RenderState
 	{
-		BinaryReader br(file);
-		uint32_t version;
-		br.read(version);
-		CE_ASSERT(version == SHADER_VERSION, "Wrong version");
+		bool _rgb_write;
+		bool _alpha_write;
+		bool _depth_write;
+		DepthTest::Enum _depth_test;
+		BlendFunction::Enum _blend_src;
+		BlendFunction::Enum _blend_dst;
+		BlendEquation::Enum _blend_equation;
+		CullMode::Enum _cull_mode;
+		PrimitiveType::Enum _primitive_type;
+
+		void reset()
+		{
+			_rgb_write = false;
+			_alpha_write = false;
+			_depth_write = false;
+			_depth_test = DepthTest::COUNT;
+			_blend_src = BlendFunction::COUNT;
+			_blend_dst = BlendFunction::COUNT;
+			_blend_equation = BlendEquation::COUNT;
+			_cull_mode = CullMode::COUNT;
+			_primitive_type = PrimitiveType::COUNT;
+		}
+
+		uint64_t encode() const
+		{
+			uint64_t state = 0;
+
+			state |= (_rgb_write ? BGFX_STATE_RGB_WRITE : 0);
+			state |= (_alpha_write ? BGFX_STATE_ALPHA_WRITE : 0);
+			state |= (_depth_write ? BGFX_STATE_DEPTH_WRITE : 0);
+
+			static uint64_t _bgfx_depth_test_map[] =
+			{
+				BGFX_STATE_DEPTH_TEST_LESS,     // DepthTest::LESS
+				BGFX_STATE_DEPTH_TEST_LEQUAL,   // DepthTest::LEQUAL
+				BGFX_STATE_DEPTH_TEST_EQUAL,    // DepthTest::EQUAL
+				BGFX_STATE_DEPTH_TEST_GEQUAL,   // DepthTest::GEQUAL
+				BGFX_STATE_DEPTH_TEST_GREATER,  // DepthTest::GREATER
+				BGFX_STATE_DEPTH_TEST_NOTEQUAL, // DepthTest::NOTEQUAL
+				BGFX_STATE_DEPTH_TEST_NEVER,    // DepthTest::NEVER
+				BGFX_STATE_DEPTH_TEST_ALWAYS,   // DepthTest::ALWAYS
+				0
+			};
 
-		uint32_t vs_code_size;
-		br.read(vs_code_size);
-		const bgfx::Memory* vsmem = bgfx::alloc(vs_code_size);
-		br.read(vsmem->data, vs_code_size);
+			static uint64_t _bgfx_blend_function_map[] =
+			{
+				BGFX_STATE_BLEND_ZERO,          // BlendFunction::ZERO
+				BGFX_STATE_BLEND_ONE,           // BlendFunction::ONE
+				BGFX_STATE_BLEND_SRC_COLOR,     // BlendFunction::SRC_COLOR
+				BGFX_STATE_BLEND_INV_SRC_COLOR, // BlendFunction::INV_SRC_COLOR
+				BGFX_STATE_BLEND_SRC_ALPHA,     // BlendFunction::SRC_ALPHA
+				BGFX_STATE_BLEND_INV_SRC_ALPHA, // BlendFunction::INV_SRC_ALPHA
+				BGFX_STATE_BLEND_DST_ALPHA,     // BlendFunction::DST_ALPHA
+				BGFX_STATE_BLEND_INV_DST_ALPHA, // BlendFunction::INV_DST_ALPHA
+				BGFX_STATE_BLEND_DST_COLOR,     // BlendFunction::DST_COLOR
+				BGFX_STATE_BLEND_INV_DST_COLOR, // BlendFunction::INV_DST_COLOR
+				BGFX_STATE_BLEND_SRC_ALPHA_SAT, // BlendFunction::SRC_ALPHA_SAT
+				BGFX_STATE_BLEND_FACTOR,        // BlendFunction::FACTOR
+				BGFX_STATE_BLEND_INV_FACTOR,    // BlendFunction::INV_FACTOR
+				0
+			};
 
-		uint32_t fs_code_size;
-		br.read(fs_code_size);
-		const bgfx::Memory* fsmem = bgfx::alloc(fs_code_size);
-		br.read(fsmem->data, fs_code_size);
+			static uint64_t _bgfx_blend_equation_map[] =
+			{
+				BGFX_STATE_BLEND_EQUATION_ADD,    // BlendEquation::ADD
+				BGFX_STATE_BLEND_EQUATION_SUB,    // BlendEquation::SUB
+				BGFX_STATE_BLEND_EQUATION_REVSUB, // BlendEquation::REVSUB
+				BGFX_STATE_BLEND_EQUATION_MIN,    // BlendEquation::MIN
+				BGFX_STATE_BLEND_EQUATION_MAX,    // BlendEquation::MAX
+				0
+			};
 
-		Shader* shader = (Shader*) a.allocate(sizeof(Shader));
-		shader->vs = vsmem;
-		shader->fs = fsmem;
-		shader->program.idx = bgfx::invalidHandle;
+			static uint64_t _bgfx_cull_mode_map[] =
+			{
+				BGFX_STATE_CULL_CW,  // CullMode::CW
+				BGFX_STATE_CULL_CCW, // CullMode::CCW
+				0
+			};
 
-		return shader;
+			static uint64_t _bgfx_primitive_type_map[] =
+			{
+				BGFX_STATE_PT_TRISTRIP,  // PrimitiveType::PT_TRISTRIP
+				BGFX_STATE_PT_LINES,     // PrimitiveType::PT_LINES
+				BGFX_STATE_PT_LINESTRIP, // PrimitiveType::PT_LINESTRIP
+				BGFX_STATE_PT_POINTS,    // PrimitiveType::PT_POINTS
+				0
+			};
+
+			state |= _bgfx_depth_test_map[_depth_test];
+			state |= BGFX_STATE_BLEND_FUNC(_bgfx_blend_function_map[_blend_src], _bgfx_blend_function_map[_blend_dst]);
+			state |= BGFX_STATE_BLEND_EQUATION(_bgfx_blend_equation_map[_blend_equation]);
+			state |= _bgfx_cull_mode_map[_cull_mode];
+			state |= _bgfx_primitive_type_map[_primitive_type];
+
+			return state;
+		}
+	};
+
+	struct BgfxShader
+	{
+		BgfxShader()
+			: _includes(default_allocator())
+			, _code(default_allocator())
+			, _vs_code(default_allocator())
+			, _fs_code(default_allocator())
+			, _varying(default_allocator())
+			, _vs_input_output(default_allocator())
+			, _fs_input_output(default_allocator())
+		{
+		}
+
+		BgfxShader(Allocator& a)
+			: _includes(a)
+			, _code(a)
+			, _vs_code(a)
+			, _fs_code(a)
+			, _varying(a)
+			, _vs_input_output(a)
+			, _fs_input_output(a)
+		{
+		}
+
+		DynamicString _includes;
+		DynamicString _code;
+		DynamicString _vs_code;
+		DynamicString _fs_code;
+		DynamicString _varying;
+		DynamicString _vs_input_output;
+		DynamicString _fs_input_output;
+
+		ALLOCATOR_AWARE;
+	};
+
+	struct ShaderPermutation
+	{
+		ShaderPermutation()
+			: _bgfx_shader(default_allocator())
+			, _render_state(default_allocator())
+		{
+		}
+
+		ShaderPermutation(Allocator& a)
+			: _bgfx_shader(a)
+			, _render_state(a)
+		{
+		}
+
+		DynamicString _bgfx_shader;
+		DynamicString _render_state;
+	};
+
+	struct ShaderCompiler
+	{
+		CompileOptions& _opts;
+		Map<DynamicString, RenderState> _render_states;
+		Map<DynamicString, BgfxShader> _bgfx_shaders;
+		Map<DynamicString, ShaderPermutation> _shaders;
+
+		ShaderCompiler(CompileOptions& opts)
+			: _opts(opts)
+			, _render_states(default_allocator())
+			, _bgfx_shaders(default_allocator())
+			, _shaders(default_allocator())
+		{
+		}
+
+		void parse(const char* path)
+		{
+			parse(_opts.read(path));
+		}
+
+		void parse(Buffer b)
+		{
+			TempAllocator4096 ta;
+			JsonObject object(ta);
+			sjson::parse(b, object);
+
+			if (map::has(object, FixedString("include")))
+			{
+				JsonArray arr(ta);
+				sjson::parse_array(object["include"], arr);
+
+				for (uint32_t i = 0; i < array::size(arr); ++i)
+				{
+					DynamicString path(ta);
+					sjson::parse_string(arr[i], path);
+					parse(path.c_str());
+				}
+			}
+
+			if (map::has(object, FixedString("render_states")))
+				parse_render_states(object["render_states"]);
+
+			if (map::has(object, FixedString("bgfx_shaders")))
+				parse_bgfx_shaders(object["bgfx_shaders"]);
+
+			if (map::has(object, FixedString("shaders")))
+				parse_shaders(object["shaders"]);
+		}
+
+		void parse_render_states(const char* json)
+		{
+			TempAllocator4096 ta;
+			JsonObject render_states(ta);
+			sjson::parse_object(json, render_states);
+
+			const typename JsonObject::Node* begin = map::begin(render_states);
+			const typename JsonObject::Node* end = map::end(render_states);
+			for (; begin != end; ++begin)
+			{
+				JsonObject obj(ta);
+				sjson::parse_object(begin->pair.second, obj);
+
+				const bool rgb_write   = sjson::parse_bool(obj["rgb_write"]);
+				const bool alpha_write = sjson::parse_bool(obj["alpha_write"]);
+				const bool depth_write = sjson::parse_bool(obj["depth_write"]);
+
+				DynamicString depth_test(ta);
+				DynamicString blend_src(ta);
+				DynamicString blend_dst(ta);
+				DynamicString blend_equation(ta);
+				DynamicString cull_mode(ta);
+				DynamicString primitive_type(ta);
+				if (map::has(obj, FixedString("depth_test")))
+					sjson::parse_string(obj["depth_test"], depth_test);
+				if (map::has(obj, FixedString("blend_src")))
+					sjson::parse_string(obj["blend_src"], blend_src);
+				if (map::has(obj, FixedString("blend_dst")))
+					sjson::parse_string(obj["blend_dst"], blend_dst);
+				if (map::has(obj, FixedString("blend_equation")))
+					sjson::parse_string(obj["blend_equation"], blend_equation);
+				if (map::has(obj, FixedString("cull_mode")))
+					sjson::parse_string(obj["cull_mode"], cull_mode);
+				if (map::has(obj, FixedString("primitive_type")))
+					sjson::parse_string(obj["primitive_type"], primitive_type);
+
+				DynamicString key(ta);
+				key = begin->pair.first;
+
+				RenderState rs;
+				rs.reset();
+				rs._rgb_write      = rgb_write;
+				rs._alpha_write    = alpha_write;
+				rs._depth_write    = depth_write;
+				rs._depth_test     = name_to_depth_test(depth_test.c_str());
+				rs._blend_src      = name_to_blend_function(blend_src.c_str());
+				rs._blend_dst      = name_to_blend_function(blend_dst.c_str());
+				rs._blend_equation = name_to_blend_equation(blend_equation.c_str());
+				rs._cull_mode      = name_to_cull_mode(cull_mode.c_str());
+				rs._primitive_type = name_to_primitive_type(primitive_type.c_str());
+
+				map::set(_render_states, key, rs);
+			}
+		}
+
+		void parse_bgfx_shaders(const char* json)
+		{
+			TempAllocator4096 ta;
+			JsonObject bgfx_shaders(ta);
+			sjson::parse_object(json, bgfx_shaders);
+
+			const typename JsonObject::Node* begin = map::begin(bgfx_shaders);
+			const typename JsonObject::Node* end = map::end(bgfx_shaders);
+			for (; begin != end; ++begin)
+			{
+				JsonObject shader(ta);
+				sjson::parse_object(begin->pair.second, shader);
+
+				BgfxShader bgfxshader(default_allocator());
+				if (map::has(shader, FixedString("includes")))
+					sjson::parse_string(shader["includes"], bgfxshader._includes);
+				if (map::has(shader, FixedString("code")))
+					sjson::parse_string(shader["code"], bgfxshader._code);
+				if (map::has(shader, FixedString("vs_code")))
+					sjson::parse_string(shader["vs_code"], bgfxshader._vs_code);
+				if (map::has(shader, FixedString("fs_code")))
+					sjson::parse_string(shader["fs_code"], bgfxshader._fs_code);
+				if (map::has(shader, FixedString("varying")))
+					sjson::parse_string(shader["varying"], bgfxshader._varying);
+				if (map::has(shader, FixedString("vs_input_output")))
+					sjson::parse_string(shader["vs_input_output"], bgfxshader._vs_input_output);
+				if (map::has(shader, FixedString("fs_input_output")))
+					sjson::parse_string(shader["fs_input_output"], bgfxshader._fs_input_output);
+
+				DynamicString key(ta);
+				key = begin->pair.first;
+
+				map::set(_bgfx_shaders, key, bgfxshader);
+			}
+		}
+
+		void parse_shaders(const char* json)
+		{
+			TempAllocator4096 ta;
+			JsonObject shaders(ta);
+			sjson::parse_object(json, shaders);
+
+			const typename JsonObject::Node* begin = map::begin(shaders);
+			const typename JsonObject::Node* end = map::end(shaders);
+			for (; begin != end; ++begin)
+			{
+				JsonObject obj(ta);
+				sjson::parse_object(begin->pair.second, obj);
+
+				ShaderPermutation shader(default_allocator());
+				sjson::parse_string(obj["bgfx_shader"], shader._bgfx_shader);
+				sjson::parse_string(obj["render_state"], shader._render_state);
+
+				DynamicString key(ta);
+				key = begin->pair.first;
+
+				map::set(_shaders, key, shader);
+			}
+		}
+
+		void compile()
+		{
+			_opts.write(SHADER_VERSION);
+			_opts.write(map::size(_shaders));
+
+			const typename Map<DynamicString, ShaderPermutation>::Node* begin = map::begin(_shaders);
+			const typename Map<DynamicString, ShaderPermutation>::Node* end = map::end(_shaders);
+			for (; begin != end; ++begin)
+			{
+				const ShaderPermutation& sp = begin->pair.second;
+				const StringId32 shader_name = begin->pair.first.to_string_id();
+				const char* bgfx_shader = sp._bgfx_shader.c_str();
+				const char* render_state = sp._render_state.c_str();
+
+				RESOURCE_COMPILER_ASSERT(map::has(_bgfx_shaders, sp._bgfx_shader)
+					, _opts
+					, "Unknown bgfx shader"
+					);
+				RESOURCE_COMPILER_ASSERT(map::has(_render_states, sp._render_state)
+					, _opts
+					, "Unknown render state"
+					);
+				const RenderState& rs = _render_states[render_state];
+
+				_opts.write(shader_name._id); // Shader name
+				_opts.write(rs.encode());     // Render state
+				compile(bgfx_shader);         // Shader code
+			}
+		}
+
+		void compile(const char* bgfx_shader)
+		{
+			const BgfxShader& shader = _bgfx_shaders[bgfx_shader];
+
+			DynamicString included_code(default_allocator());
+			if (!(shader._includes == ""))
+			{
+				const BgfxShader& included = _bgfx_shaders[shader._includes.c_str()];
+				included_code = included._code;
+			}
+
+			DynamicString vs_code(default_allocator());
+			DynamicString fs_code(default_allocator());
+			vs_code += shader._vs_input_output;
+			vs_code += included_code;
+			vs_code += shader._code;
+			vs_code += shader._vs_code;
+			fs_code += shader._fs_input_output;
+			fs_code += included_code;
+			fs_code += shader._code;
+			fs_code += shader._fs_code;
+
+			DynamicString vs_code_path(default_allocator());
+			DynamicString fs_code_path(default_allocator());
+			DynamicString varying_def_path(default_allocator());
+			DynamicString tmpvs_path(default_allocator());
+			DynamicString tmpfs_path(default_allocator());
+
+			_opts.get_absolute_path("vs_code.tmp", vs_code_path);
+			_opts.get_absolute_path("fs_code.tmp", fs_code_path);
+			_opts.get_absolute_path("varying.tmp", varying_def_path);
+			_opts.get_absolute_path("tmpvs", tmpvs_path);
+			_opts.get_absolute_path("tmpfs", tmpfs_path);
+
+			File* vs_file = _opts._fs.open(vs_code_path.c_str(), FileOpenMode::WRITE);
+			vs_file->write(vs_code.c_str(), vs_code.length());
+			_opts._fs.close(*vs_file);
+
+			File* fs_file = _opts._fs.open(fs_code_path.c_str(), FileOpenMode::WRITE);
+			fs_file->write(fs_code.c_str(), fs_code.length());
+			_opts._fs.close(*fs_file);
+
+			File* varying_file = _opts._fs.open(varying_def_path.c_str(), FileOpenMode::WRITE);
+			varying_file->write(shader._varying.c_str(), shader._varying.length());
+			_opts._fs.close(*varying_file);
+
+			TempAllocator4096 ta;
+			StringStream output(ta);
+			using namespace string_stream;
+
+			int exitcode = run_external_compiler(vs_code_path.c_str()
+				, tmpvs_path.c_str()
+				, varying_def_path.c_str()
+				, "vertex"
+				, _opts.platform()
+				, output
+				);
+			RESOURCE_COMPILER_ASSERT(exitcode == 0
+				, _opts
+				, "Failed to compile vertex shader:\n%s"
+				, c_str(output)
+				);
+
+			array::clear(output);
+			exitcode = run_external_compiler(fs_code_path.c_str()
+				, tmpfs_path.c_str()
+				, varying_def_path.c_str()
+				, "fragment"
+				, _opts.platform()
+				, output
+				);
+			if (exitcode)
+			{
+				_opts.delete_file(tmpvs_path.c_str());
+				RESOURCE_COMPILER_ASSERT(false
+					, _opts
+					, "Failed to compile fragment shader:\n%s"
+					, c_str(output)
+					);
+			}
+
+			_opts.delete_file(vs_code_path.c_str());
+			_opts.delete_file(fs_code_path.c_str());
+			_opts.delete_file(varying_def_path.c_str());
+
+			Buffer tmpvs = _opts.read(tmpvs_path.c_str());
+			Buffer tmpfs = _opts.read(tmpfs_path.c_str());
+
+			_opts.delete_file(tmpvs_path.c_str());
+			_opts.delete_file(tmpfs_path.c_str());
+
+			// Write
+			_opts.write(uint32_t(array::size(tmpvs)));
+			_opts.write(array::begin(tmpvs), array::size(tmpvs));
+			_opts.write(uint32_t(array::size(tmpfs)));
+			_opts.write(array::begin(tmpfs), array::size(tmpfs));
+		}
+	};
+
+	void compile(const char* path, CompileOptions& opts)
+	{
+		ShaderCompiler sc(opts);
+		sc.parse(path);
+		sc.compile();
+	}
+
+	void* load(File& file, Allocator& a)
+	{
+		return device()->shader_manager()->load(file, a);
 	}
 
 	void online(StringId64 id, ResourceManager& rm)
 	{
-		Shader* shader = (Shader*) rm.get(SHADER_TYPE, id);
-		bgfx::ShaderHandle vs = bgfx::createShader(shader->vs);
-		CE_ASSERT(bgfx::isValid(vs), "Failed to create vertex shader");
-		bgfx::ShaderHandle fs = bgfx::createShader(shader->fs);
-		CE_ASSERT(bgfx::isValid(fs), "Failed to create fragment shader");
-		shader->program = bgfx::createProgram(vs, fs, true);
-		CE_ASSERT(bgfx::isValid(shader->program), "Failed to create GPU program");
+		device()->shader_manager()->online(id, rm);
 	}
 
 	void offline(StringId64 id, ResourceManager& rm)
 	{
-		Shader* shader = (Shader*) rm.get(SHADER_TYPE, id);
-		bgfx::destroyProgram(shader->program);
+		device()->shader_manager()->offline(id, rm);
 	}
 
 	void unload(Allocator& a, void* res)
 	{
-		a.deallocate(res);
+		device()->shader_manager()->unload(a, res);
 	}
 } // namespace shader_resource
 } // namespace crown

+ 16 - 4
src/renderers/shader.h

@@ -11,16 +11,28 @@
 #include "memory_types.h"
 #include "compiler_types.h"
 #include "string_id.h"
+#include "container_types.h"
 #include <bgfx/bgfx.h>
 
 namespace crown
 {
 
-struct Shader
+struct ShaderResource
 {
-	const bgfx::Memory* vs;
-	const bgfx::Memory* fs;
-	bgfx::ProgramHandle program;
+	ShaderResource(Allocator& a)
+		: _data(a)
+	{
+	}
+
+	struct Data
+	{
+		StringId32 name;
+		uint64_t state;
+		const bgfx::Memory* vsmem;
+		const bgfx::Memory* fsmem;
+	};
+
+	Array<Data> _data;
 };
 
 namespace shader_resource

+ 13 - 6
src/renderers/shader_manager.cpp

@@ -37,6 +37,9 @@ void* ShaderManager::load(File& file, Allocator& a)
 		uint32_t shader_name;
 		br.read(shader_name);
 
+		uint64_t render_state;
+		br.read(render_state);
+
 		uint32_t vs_code_size;
 		br.read(vs_code_size);
 		const bgfx::Memory* vsmem = bgfx::alloc(vs_code_size);
@@ -48,6 +51,7 @@ void* ShaderManager::load(File& file, Allocator& a)
 		br.read(fsmem->data, fs_code_size);
 
 		sr->_data[i].name._id = shader_name;
+		sr->_data[i].state = render_state;
 		sr->_data[i].vsmem = vsmem;
 		sr->_data[i].fsmem = fsmem;
 	}
@@ -70,7 +74,7 @@ void ShaderManager::online(StringId64 id, ResourceManager& rm)
 		bgfx::ProgramHandle program = bgfx::createProgram(vs, fs, true);
 		CE_ASSERT(bgfx::isValid(program), "Failed to create GPU program");
 
-		add_shader(data.name, program);
+		add_shader(data.name, data.state, program);
 	}
 }
 
@@ -81,8 +85,9 @@ void ShaderManager::offline(StringId64 id, ResourceManager& rm)
 	for (uint32_t i = 0; i < array::size(shader->_data); ++i)
 	{
 		const ShaderResource::Data& data = shader->_data[i];
+		const ShaderData& sd = get(data.name);
 
-		bgfx::destroyProgram(get(data.name));
+		bgfx::destroyProgram(sd.program);
 
 		sort_map::remove(_shader_map, data.name);
 		sort_map::sort(_shader_map);
@@ -94,20 +99,22 @@ void ShaderManager::unload(Allocator& a, void* res)
 	CE_DELETE(a, (ShaderResource*)res);
 }
 
-void ShaderManager::add_shader(StringId32 name, bgfx::ProgramHandle program)
+void ShaderManager::add_shader(StringId32 name, uint64_t state, bgfx::ProgramHandle program)
 {
 	ShaderData sd;
+	sd.state = state;
 	sd.program = program;
 	sort_map::set(_shader_map, name, sd);
 	sort_map::sort(_shader_map);
 }
 
-bgfx::ProgramHandle ShaderManager::get(StringId32 shader)
+const ShaderManager::ShaderData& ShaderManager::get(StringId32 id)
 {
-	CE_ASSERT(sort_map::has(_shader_map, shader), "Shader not found");
+	CE_ASSERT(sort_map::has(_shader_map, id), "Shader not found");
 	ShaderData deffault;
+	deffault.state = BGFX_STATE_DEFAULT;
 	deffault.program = BGFX_INVALID_HANDLE;
-	return sort_map::get(_shader_map, shader, deffault).program;
+	return sort_map::get(_shader_map, id, deffault);
 }
 
 } // namespace crown

+ 11 - 7
src/renderers/shader_manager.h

@@ -17,6 +17,14 @@ namespace crown
 
 class ShaderManager
 {
+public:
+
+	struct ShaderData
+	{
+		uint64_t state;
+		bgfx::ProgramHandle program;
+	};
+
 public:
 
 	ShaderManager(Allocator& a);
@@ -26,19 +34,15 @@ public:
 	void offline(StringId64 id, ResourceManager& rm);
 	void unload(Allocator& a, void* res);
 
-	bgfx::ProgramHandle get(StringId32 shader);
+	/// Returns the shader @a id.
+	const ShaderData& get(StringId32 id);
 
 private:
 
-	void add_shader(StringId32 name, bgfx::ProgramHandle program);
+	void add_shader(StringId32 name, uint64_t state, bgfx::ProgramHandle program);
 
 private:
 
-	struct ShaderData
-	{
-		bgfx::ProgramHandle program;
-	};
-
 	typedef SortMap<StringId32, ShaderData> ShaderMap;
 
 	ShaderMap _shader_map;

+ 0 - 7
src/renderers/sprite.cpp

@@ -45,13 +45,6 @@ void Sprite::set_depth(int32_t z)
 
 void Sprite::render()
 {
-	bgfx::setState(BGFX_STATE_RGB_WRITE
-		| BGFX_STATE_ALPHA_WRITE
-		| BGFX_STATE_DEPTH_TEST_LEQUAL
-		| BGFX_STATE_DEPTH_WRITE
-		| BGFX_STATE_CULL_CW
-		| BGFX_STATE_MSAA
-		| BGFX_STATE_BLEND_ALPHA);
 	bgfx::setVertexBuffer(m_resource->vb);
 	bgfx::setIndexBuffer(m_resource->ib, m_frame * 6, 6);
 	TransformInstance ti = m_scene_graph.get(_unit_id);

+ 2 - 2
src/resource/material_resource.cpp

@@ -155,7 +155,7 @@ namespace material_resource
 		Array<char> names(default_allocator());
 		Array<char> dynblob(default_allocator());
 
-		ResourceId shader = root.key("shader").to_resource_id();
+		StringId32 shader = root.key("shader").to_string_id();
 		parse_textures(root, texdata, names, dynblob);
 		parse_uniforms(root, unidata, names, dynblob);
 
@@ -268,7 +268,7 @@ namespace material_resource
 		return mr->dynamic_data_offset;
 	}
 
-	StringId64 shader(const MaterialResource* mr)
+	StringId32 shader(const MaterialResource* mr)
 	{
 		return mr->shader;
 	}

+ 2 - 2
src/resource/material_resource.h

@@ -19,7 +19,7 @@ struct MaterialResource
 {
 	uint32_t version;
 	uint32_t _pad;
-	StringId64 shader;
+	StringId32 shader;
 	uint32_t num_textures;
 	uint32_t texture_data_offset;
 	uint32_t num_uniforms;
@@ -78,7 +78,7 @@ namespace material_resource
 
 	uint32_t dynamic_data_size(const MaterialResource* mr);
 	uint32_t dynamic_data_offset(const MaterialResource* mr);
-	StringId64 shader(const MaterialResource* mr);
+	StringId32 shader(const MaterialResource* mr);
 	uint32_t num_textures(const MaterialResource* mr);
 	uint32_t num_uniforms(const MaterialResource* mr);
 	UniformData* get_uniform_data(const MaterialResource* mr, uint32_t i);