Exemplo n.º 1
0
bool GLGSRender::check_program_state()
{
	auto rtt_lookup_func = [this](u32 texaddr, rsx::fragment_texture &tex, bool is_depth) -> std::tuple<bool, u16>
	{
		gl::render_target *surface = nullptr;
		if (!is_depth)
			surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr);
		else
			surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr);

		if (!surface)
		{
			auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, 0, 0, tex.pitch());
			if (!rsc.surface || rsc.is_depth_surface != is_depth)
				return std::make_tuple(false, 0);

			surface = rsc.surface;
		}

		return std::make_tuple(true, surface->get_native_pitch());
	};

	get_current_fragment_program(rtt_lookup_func);

	if (current_fragment_program.valid == false)
		return false;

	get_current_vertex_program();
	return true;
}
Exemplo n.º 2
0
void D3D12GSRender::load_program()
{
	m_vertex_program = get_current_vertex_program();
	m_fragment_program = get_current_fragment_program();

	D3D12PipelineProperties prop = {};
	prop.Topology = get_primitive_topology_type(draw_mode);

	static D3D12_BLEND_DESC CD3D12_BLEND_DESC =
	{
		FALSE,
		FALSE,
		{
			FALSE,FALSE,
			D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
		D3D12_LOGIC_OP_NOOP,
		D3D12_COLOR_WRITE_ENABLE_ALL,
		}
	};
	prop.Blend = CD3D12_BLEND_DESC;

	if (rsx::method_registers[NV4097_SET_BLEND_ENABLE])
	{
		prop.Blend.RenderTarget[0].BlendEnable = true;

		if (rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] & 0x2)
			prop.Blend.RenderTarget[1].BlendEnable = true;
		if (rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] & 0x4)
			prop.Blend.RenderTarget[2].BlendEnable = true;
		if (rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] & 0x8)
			prop.Blend.RenderTarget[3].BlendEnable = true;

		prop.Blend.RenderTarget[0].BlendOp = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] & 0xFFFF);
		prop.Blend.RenderTarget[0].BlendOpAlpha = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] >> 16);

		if (rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] & 0x2)
		{
			prop.Blend.RenderTarget[1].BlendOp = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] & 0xFFFF);
			prop.Blend.RenderTarget[1].BlendOpAlpha = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] >> 16);
		}

		if (rsx::method_registers[NV4097_SET_BLEND_ENABLE_MRT] & 0x4)
		{
			prop.Blend.RenderTarget[2].BlendOp = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] & 0xFFFF);
			prop.Blend.RenderTarget[2].BlendOpAlpha = get_blend_op(rsx::method_registers[NV4097_SET_BLEND_EQUATION] >> 16);
		}
Exemplo n.º 3
0
void D3D12GSRender::load_program()
{
	auto rtt_lookup_func = [this](u32 texaddr, rsx::fragment_texture&, bool is_depth) -> std::tuple<bool, u16>
	{
		ID3D12Resource *surface = nullptr;
		if (!is_depth)
			surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr);
		else
			surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr);

		if (!surface) return std::make_tuple(false, 0);
		
		D3D12_RESOURCE_DESC desc = surface->GetDesc();
		u16 native_pitch = get_dxgi_texel_size(desc.Format) * (u16)desc.Width;
		return std::make_tuple(true, native_pitch);
	};

	get_current_vertex_program({}, true);
	get_current_fragment_program_legacy(rtt_lookup_func);

	if (!current_fragment_program.valid)
		return;

	D3D12PipelineProperties prop = {};
	prop.Topology = get_primitive_topology_type(rsx::method_registers.current_draw_clause.primitive);

	static D3D12_BLEND_DESC CD3D12_BLEND_DESC =
	{
		FALSE,
		FALSE,
		{
			FALSE,FALSE,
			D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
		D3D12_LOGIC_OP_NOOP,
		D3D12_COLOR_WRITE_ENABLE_ALL,
		}
	};
	prop.Blend = CD3D12_BLEND_DESC;

	if (rsx::method_registers.blend_enabled())
	{
		//We can use the d3d blend factor as long as !(rgb_factor == alpha && a_factor == color)
		rsx::blend_factor sfactor_rgb = rsx::method_registers.blend_func_sfactor_rgb();
		rsx::blend_factor dfactor_rgb = rsx::method_registers.blend_func_dfactor_rgb();
		rsx::blend_factor sfactor_a = rsx::method_registers.blend_func_sfactor_a();
		rsx::blend_factor dfactor_a = rsx::method_registers.blend_func_dfactor_a();

		D3D12_BLEND d3d_sfactor_rgb = get_blend_factor(sfactor_rgb);
		D3D12_BLEND d3d_dfactor_rgb = get_blend_factor(dfactor_rgb);;
		D3D12_BLEND d3d_sfactor_alpha = get_blend_factor_alpha(sfactor_a);
		D3D12_BLEND d3d_dfactor_alpha = get_blend_factor_alpha(dfactor_a);
		
		auto BlendColor = rsx::get_constant_blend_colors();
		bool color_blend_possible = true;

		if (sfactor_rgb == rsx::blend_factor::constant_alpha ||
			dfactor_rgb == rsx::blend_factor::constant_alpha)
		{
			if (sfactor_rgb == rsx::blend_factor::constant_color ||
				dfactor_rgb == rsx::blend_factor::constant_color)
			{
				//Color information will be destroyed
				color_blend_possible = false;
			}
			else
			{
				//All components are alpha.
				//If an alpha factor refers to constant_color, it only refers to the alpha component, so no need to replace it
				BlendColor[0] = BlendColor[1] = BlendColor[2] = BlendColor[3];
			}
		}

		if (!color_blend_possible)
		{
			LOG_ERROR(RSX, "The constant_color blend factor combination defined is not supported");
			
			auto flatten_d3d12_factor = [](D3D12_BLEND in) -> D3D12_BLEND
			{
				switch (in)
				{
				case D3D12_BLEND_BLEND_FACTOR:
					return D3D12_BLEND_ONE;
				case D3D12_BLEND_INV_BLEND_FACTOR:
					return D3D12_BLEND_ZERO;
				}

				LOG_ERROR(RSX, "No suitable conversion defined for blend factor 0x%X" HERE, (u32)in);
				return in;
			};

			d3d_sfactor_rgb = flatten_d3d12_factor(d3d_sfactor_rgb);
			d3d_dfactor_rgb = flatten_d3d12_factor(d3d_dfactor_rgb);;
			d3d_sfactor_alpha = flatten_d3d12_factor(d3d_sfactor_alpha);
			d3d_dfactor_alpha = flatten_d3d12_factor(d3d_dfactor_alpha);
		}
		else
		{
			get_current_resource_storage().command_list->OMSetBlendFactor(BlendColor.data());
		}

		prop.Blend.RenderTarget[0].BlendEnable = true;

		if (rsx::method_registers.blend_enabled_surface_1())
			prop.Blend.RenderTarget[1].BlendEnable = true;
		if (rsx::method_registers.blend_enabled_surface_2())
			prop.Blend.RenderTarget[2].BlendEnable = true;
		if (rsx::method_registers.blend_enabled_surface_3())
			prop.Blend.RenderTarget[3].BlendEnable = true;

		prop.Blend.RenderTarget[0].BlendOp = get_blend_op(rsx::method_registers.blend_equation_rgb());
		prop.Blend.RenderTarget[0].BlendOpAlpha = get_blend_op(rsx::method_registers.blend_equation_a());

		if (rsx::method_registers.blend_enabled_surface_1())
		{
			prop.Blend.RenderTarget[1].BlendOp = get_blend_op(rsx::method_registers.blend_equation_rgb());
			prop.Blend.RenderTarget[1].BlendOpAlpha = get_blend_op(rsx::method_registers.blend_equation_a());
		}

		if (rsx::method_registers.blend_enabled_surface_2())
		{
			prop.Blend.RenderTarget[2].BlendOp = get_blend_op(rsx::method_registers.blend_equation_rgb());
			prop.Blend.RenderTarget[2].BlendOpAlpha = get_blend_op(rsx::method_registers.blend_equation_a());
		}

		if (rsx::method_registers.blend_enabled_surface_3())
		{
			prop.Blend.RenderTarget[3].BlendOp = get_blend_op(rsx::method_registers.blend_equation_rgb());
			prop.Blend.RenderTarget[3].BlendOpAlpha = get_blend_op(rsx::method_registers.blend_equation_a());
		}

		prop.Blend.RenderTarget[0].SrcBlend = d3d_sfactor_rgb;
		prop.Blend.RenderTarget[0].DestBlend = d3d_dfactor_rgb;
		prop.Blend.RenderTarget[0].SrcBlendAlpha = d3d_sfactor_alpha;
		prop.Blend.RenderTarget[0].DestBlendAlpha = d3d_dfactor_alpha;

		if (rsx::method_registers.blend_enabled_surface_1())
		{
			prop.Blend.RenderTarget[1].SrcBlend = d3d_sfactor_rgb;
			prop.Blend.RenderTarget[1].DestBlend = d3d_dfactor_rgb;
			prop.Blend.RenderTarget[1].SrcBlendAlpha = d3d_sfactor_alpha;
			prop.Blend.RenderTarget[1].DestBlendAlpha = d3d_dfactor_alpha;
		}

		if (rsx::method_registers.blend_enabled_surface_2())
		{
			prop.Blend.RenderTarget[2].SrcBlend = d3d_sfactor_rgb;
			prop.Blend.RenderTarget[2].DestBlend = d3d_dfactor_rgb;
			prop.Blend.RenderTarget[2].SrcBlendAlpha = d3d_sfactor_alpha;
			prop.Blend.RenderTarget[2].DestBlendAlpha = d3d_dfactor_alpha;
		}

		if (rsx::method_registers.blend_enabled_surface_3())
		{
			prop.Blend.RenderTarget[3].SrcBlend = d3d_sfactor_rgb;
			prop.Blend.RenderTarget[3].DestBlend = d3d_dfactor_rgb;
			prop.Blend.RenderTarget[3].SrcBlendAlpha = d3d_sfactor_alpha;
			prop.Blend.RenderTarget[3].DestBlendAlpha = d3d_dfactor_alpha;
		}
	}

	if (rsx::method_registers.logic_op_enabled())
	{
		prop.Blend.RenderTarget[0].LogicOpEnable = true;
		prop.Blend.RenderTarget[0].LogicOp = get_logic_op(rsx::method_registers.logic_operation());
	}

	prop.DepthStencilFormat = get_depth_stencil_surface_format(rsx::method_registers.surface_depth_fmt());
	prop.RenderTargetsFormat = get_color_surface_format(rsx::method_registers.surface_color());

	switch (rsx::method_registers.surface_color_target())
	{
	case rsx::surface_target::surface_a:
	case rsx::surface_target::surface_b:
		prop.numMRT = 1;
		break;
	case rsx::surface_target::surfaces_a_b:
		prop.numMRT = 2;
		break;
	case rsx::surface_target::surfaces_a_b_c:
		prop.numMRT = 3;
		break;
	case rsx::surface_target::surfaces_a_b_c_d:
		prop.numMRT = 4;
		break;
	default:
		break;
	}
	if (rsx::method_registers.depth_test_enabled())
	{
		prop.DepthStencil.DepthEnable = TRUE;
		prop.DepthStencil.DepthFunc = get_compare_func(rsx::method_registers.depth_func());
	}
	else
		prop.DepthStencil.DepthEnable = FALSE;

	prop.DepthStencil.DepthWriteMask = rsx::method_registers.depth_write_enabled() ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;

	if (rsx::method_registers.stencil_test_enabled())
	{
		prop.DepthStencil.StencilEnable = TRUE;
		prop.DepthStencil.StencilReadMask = rsx::method_registers.stencil_func_mask();
		prop.DepthStencil.StencilWriteMask = rsx::method_registers.stencil_mask();
		prop.DepthStencil.FrontFace.StencilPassOp = get_stencil_op(rsx::method_registers.stencil_op_zpass());
		prop.DepthStencil.FrontFace.StencilDepthFailOp = get_stencil_op(rsx::method_registers.stencil_op_zfail());
		prop.DepthStencil.FrontFace.StencilFailOp = get_stencil_op(rsx::method_registers.stencil_op_fail());
		prop.DepthStencil.FrontFace.StencilFunc = get_compare_func(rsx::method_registers.stencil_func());

		if (rsx::method_registers.two_sided_stencil_test_enabled())
		{
			prop.DepthStencil.BackFace.StencilFailOp = get_stencil_op(rsx::method_registers.back_stencil_op_fail());
			prop.DepthStencil.BackFace.StencilFunc = get_compare_func(rsx::method_registers.back_stencil_func());
			prop.DepthStencil.BackFace.StencilPassOp = get_stencil_op(rsx::method_registers.back_stencil_op_zpass());
			prop.DepthStencil.BackFace.StencilDepthFailOp = get_stencil_op(rsx::method_registers.back_stencil_op_zfail());
		}
		else
		{
			prop.DepthStencil.BackFace.StencilPassOp = get_stencil_op(rsx::method_registers.stencil_op_zpass());
			prop.DepthStencil.BackFace.StencilDepthFailOp = get_stencil_op(rsx::method_registers.stencil_op_zfail());
			prop.DepthStencil.BackFace.StencilFailOp = get_stencil_op(rsx::method_registers.stencil_op_fail());
			prop.DepthStencil.BackFace.StencilFunc = get_compare_func(rsx::method_registers.stencil_func());
		}
	}

	// Sensible default value
	static D3D12_RASTERIZER_DESC CD3D12_RASTERIZER_DESC =
	{
		D3D12_FILL_MODE_SOLID,
		D3D12_CULL_MODE_NONE,
		FALSE,
		D3D12_DEFAULT_DEPTH_BIAS,
		D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
		D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
		TRUE,
		FALSE,
		FALSE,
		0,
		D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
	};
	prop.Rasterization = CD3D12_RASTERIZER_DESC;
	prop.Rasterization.DepthClipEnable = rsx::method_registers.depth_clip_enabled();

	if (rsx::method_registers.cull_face_enabled())
	{
		prop.Rasterization.CullMode = get_cull_face(rsx::method_registers.cull_face_mode());
	}

	prop.Rasterization.FrontCounterClockwise = get_front_face_ccw(rsx::method_registers.front_face_mode());

	UINT8 mask = 0;
	mask |= rsx::method_registers.color_mask_r() ? D3D12_COLOR_WRITE_ENABLE_RED : 0;
	mask |= rsx::method_registers.color_mask_g() ? D3D12_COLOR_WRITE_ENABLE_GREEN : 0;
	mask |= rsx::method_registers.color_mask_b() ? D3D12_COLOR_WRITE_ENABLE_BLUE : 0;
	mask |= rsx::method_registers.color_mask_a() ? D3D12_COLOR_WRITE_ENABLE_ALPHA : 0;
	for (unsigned i = 0; i < prop.numMRT; i++)
		prop.Blend.RenderTarget[i].RenderTargetWriteMask = mask;

	if (rsx::method_registers.restart_index_enabled())
	{
		rsx::index_array_type index_type = rsx::method_registers.current_draw_clause.is_immediate_draw?
			rsx::index_array_type::u32:
			rsx::method_registers.index_type();

		if (index_type == rsx::index_array_type::u32)
		{
			prop.CutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF;
		}
		if (index_type == rsx::index_array_type::u16)
		{
			prop.CutValue = D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF;
		}
	}

	m_current_pso = m_pso_cache.get_graphics_pipeline(current_vertex_program, current_fragment_program, prop, false, m_device.Get(), m_shared_root_signature.Get());
	return;
}
Exemplo n.º 4
0
bool GLGSRender::load_program()
{
	RSXVertexProgram vertex_program = get_current_vertex_program();
	RSXFragmentProgram fragment_program = get_current_fragment_program();

	for (auto &vtx : vertex_program.rsx_vertex_inputs)
	{
		auto &array_info = rsx::method_registers.vertex_arrays_info[vtx.location];
		if (array_info.type() == rsx::vertex_base_type::s1 ||
			array_info.type() == rsx::vertex_base_type::cmp)
		{
			//Some vendors do not support GL_x_SNORM buffer textures
			verify(HERE), vtx.flags == 0;
			vtx.flags |= GL_VP_FORCE_ATTRIB_SCALING | GL_VP_ATTRIB_S16_INT;
		}
	}

	for (int i = 0; i < 16; ++i)
	{
		auto &tex = rsx::method_registers.fragment_textures[i];
		if (tex.enabled())
		{
			const u32 texaddr = rsx::get_address(tex.offset(), tex.location());
			if (m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr))
			{
				//Ignore this rtt since we have an aloasing color texture that will be used
				if (m_rtts.get_texture_from_render_target_if_applicable(texaddr))
					continue;

				u32 format = tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
				if (format == CELL_GCM_TEXTURE_A8R8G8B8 || format == CELL_GCM_TEXTURE_D8R8G8B8)
				{
					fragment_program.redirected_textures |= (1 << i);
				}
			}
		}
	}

	auto old_program = m_program;
	m_program = &m_prog_buffer.getGraphicPipelineState(vertex_program, fragment_program, nullptr);
	m_program->use();

	//Apps can write into the fragment program binary.
	u32 fragment_constants_size = m_prog_buffer.get_fragment_constants_buffer_size(fragment_program);
	std::vector<u8> fragment_constants_buf;

	if (fragment_constants_size)
	{
		fragment_constants_buf.resize(fragment_constants_size);
		m_prog_buffer.fill_fragment_constants_buffer({ reinterpret_cast<float*>(fragment_constants_buf.data()), gsl::narrow<int>(fragment_constants_size) }, fragment_program);
	}

	if (old_program == m_program && !m_transform_constants_dirty)
	{
		//This path is taken alot so the savings are tangible
		struct scale_offset_layout
		{
			u16 clip_w, clip_h;
			float scale_x, offset_x, scale_y, offset_y, scale_z, offset_z;
			float fog0, fog1;
			u32   alpha_tested;
			float alpha_ref;
		}
		tmp = {};
		
		tmp.clip_w = rsx::method_registers.surface_clip_width();
		tmp.clip_h = rsx::method_registers.surface_clip_height();
		tmp.scale_x = rsx::method_registers.viewport_scale_x();
		tmp.offset_x = rsx::method_registers.viewport_offset_x();
		tmp.scale_y = rsx::method_registers.viewport_scale_y();
		tmp.offset_y = rsx::method_registers.viewport_offset_y();
		tmp.scale_z = rsx::method_registers.viewport_scale_z();
		tmp.offset_z = rsx::method_registers.viewport_offset_z();
		tmp.fog0 = rsx::method_registers.fog_params_0();
		tmp.fog1 = rsx::method_registers.fog_params_1();
		tmp.alpha_tested = rsx::method_registers.alpha_test_enabled();
		tmp.alpha_ref = rsx::method_registers.alpha_ref();

		size_t old_hash = m_transform_buffer_hash;
		m_transform_buffer_hash = 0;

		u8 *data = reinterpret_cast<u8*>(&tmp);
		for (int i = 0; i < sizeof(tmp); ++i)
			m_transform_buffer_hash ^= std::hash<char>()(data[i]);

		if (old_hash == m_transform_buffer_hash)
		{
			//Its likely that nothing changed since previous draw.
			if (!fragment_constants_size)
				return true;

			old_hash = m_fragment_buffer_hash;
			m_fragment_buffer_hash = 0;

			for (int i = 0; i < fragment_constants_size; ++i)
				m_fragment_buffer_hash ^= std::hash<char>()(fragment_constants_buf[i]);

			if (m_fragment_buffer_hash == old_hash)
				return true;
		}
	}

	m_transform_constants_dirty = false;

	fragment_constants_size = std::max(32U, fragment_constants_size);
	u32 max_buffer_sz = 512 + 8192 + align(fragment_constants_size, m_uniform_buffer_offset_align);

	if (manually_flush_ring_buffers)
		m_uniform_ring_buffer->reserve_storage_on_heap(align(max_buffer_sz, 512));

	u8 *buf;
	u32 scale_offset_offset;
	u32 vertex_constants_offset;
	u32 fragment_constants_offset;

	// Scale offset
	auto mapping = m_uniform_ring_buffer->alloc_from_heap(512, m_uniform_buffer_offset_align);
	buf = static_cast<u8*>(mapping.first);
	scale_offset_offset = mapping.second;
	fill_scale_offset_data(buf, false);

	// Fragment state 
	u32 is_alpha_tested = rsx::method_registers.alpha_test_enabled();
	float alpha_ref = rsx::method_registers.alpha_ref() / 255.f;
	f32 fog0 = rsx::method_registers.fog_params_0();
	f32 fog1 = rsx::method_registers.fog_params_1();
	memcpy(buf + 16 * sizeof(float), &fog0, sizeof(float));
	memcpy(buf + 17 * sizeof(float), &fog1, sizeof(float));
	memcpy(buf + 18 * sizeof(float), &is_alpha_tested, sizeof(u32));
	memcpy(buf + 19 * sizeof(float), &alpha_ref, sizeof(float));

	// Vertex constants
	mapping = m_uniform_ring_buffer->alloc_from_heap(8192, m_uniform_buffer_offset_align);
	buf = static_cast<u8*>(mapping.first);
	vertex_constants_offset = mapping.second;
	fill_vertex_program_constants_data(buf);

	// Fragment constants
	if (fragment_constants_size)
	{
		mapping = m_uniform_ring_buffer->alloc_from_heap(fragment_constants_size, m_uniform_buffer_offset_align);
		buf = static_cast<u8*>(mapping.first);
		fragment_constants_offset = mapping.second;
		memcpy(buf, fragment_constants_buf.data(), fragment_constants_buf.size());
	}

	m_uniform_ring_buffer->bind_range(0, scale_offset_offset, 512);
	m_uniform_ring_buffer->bind_range(1, vertex_constants_offset, 8192);
	if (fragment_constants_size)
	{
		m_uniform_ring_buffer->bind_range(2, fragment_constants_offset, fragment_constants_size);
	}

	if (manually_flush_ring_buffers)
		m_uniform_ring_buffer->unmap();

	return true;
}
Exemplo n.º 5
0
bool GLGSRender::load_program()
{
	auto rtt_lookup_func = [this](u32 texaddr, bool is_depth) -> std::tuple<bool, u16>
	{
		gl::render_target *surface = nullptr;
		if (!is_depth)
			surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr);
		else
			surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr);

		if (!surface) return std::make_tuple(false, 0);
		return std::make_tuple(true, surface->get_native_pitch());
	};

	RSXVertexProgram vertex_program = get_current_vertex_program();
	RSXFragmentProgram fragment_program = get_current_fragment_program(rtt_lookup_func);

	std::array<float, 16> rtt_scaling;
	u32 unnormalized_rtts = 0;

	for (auto &vtx : vertex_program.rsx_vertex_inputs)
	{
		auto &array_info = rsx::method_registers.vertex_arrays_info[vtx.location];
		if (array_info.type() == rsx::vertex_base_type::s1 ||
			array_info.type() == rsx::vertex_base_type::cmp)
		{
			//Some vendors do not support GL_x_SNORM buffer textures
			verify(HERE), vtx.flags == 0;
			vtx.flags |= GL_VP_FORCE_ATTRIB_SCALING | GL_VP_ATTRIB_S16_INT;
		}
	}

	auto old_program = m_program;
	m_program = &m_prog_buffer.getGraphicPipelineState(vertex_program, fragment_program, nullptr);
	m_program->use();

	if (old_program == m_program && !m_transform_constants_dirty)
	{
		//This path is taken alot so the savings are tangible
		struct scale_offset_layout
		{
			u16 clip_w, clip_h;
			float scale_x, offset_x, scale_y, offset_y, scale_z, offset_z;
			float fog0, fog1;
			u32   alpha_tested;
			float alpha_ref;
		}
		tmp = {};
		
		tmp.clip_w = rsx::method_registers.surface_clip_width();
		tmp.clip_h = rsx::method_registers.surface_clip_height();
		tmp.scale_x = rsx::method_registers.viewport_scale_x();
		tmp.offset_x = rsx::method_registers.viewport_offset_x();
		tmp.scale_y = rsx::method_registers.viewport_scale_y();
		tmp.offset_y = rsx::method_registers.viewport_offset_y();
		tmp.scale_z = rsx::method_registers.viewport_scale_z();
		tmp.offset_z = rsx::method_registers.viewport_offset_z();
		tmp.fog0 = rsx::method_registers.fog_params_0();
		tmp.fog1 = rsx::method_registers.fog_params_1();
		tmp.alpha_tested = rsx::method_registers.alpha_test_enabled();
		tmp.alpha_ref = rsx::method_registers.alpha_ref();

		size_t old_hash = m_transform_buffer_hash;
		m_transform_buffer_hash = 0;

		u8 *data = reinterpret_cast<u8*>(&tmp);
		for (int i = 0; i < sizeof(tmp); ++i)
			m_transform_buffer_hash ^= std::hash<char>()(data[i]);

		if (old_hash == m_transform_buffer_hash)
			return true;
	}

	m_transform_constants_dirty = false;

	u32 fragment_constants_size = m_prog_buffer.get_fragment_constants_buffer_size(fragment_program);
	u32 fragment_buffer_size = fragment_constants_size + (17 * 4 * sizeof(float));
	u32 max_buffer_sz = 512 + 8192 + align(fragment_constants_size, m_uniform_buffer_offset_align);

	if (manually_flush_ring_buffers)
		m_uniform_ring_buffer->reserve_storage_on_heap(align(max_buffer_sz, 512));

	u8 *buf;
	u32 scale_offset_offset;
	u32 vertex_constants_offset;
	u32 fragment_constants_offset;

	// Scale offset
	auto mapping = m_uniform_ring_buffer->alloc_from_heap(512, m_uniform_buffer_offset_align);
	buf = static_cast<u8*>(mapping.first);
	scale_offset_offset = mapping.second;
	fill_scale_offset_data(buf, false);

	// Vertex constants
	mapping = m_uniform_ring_buffer->alloc_from_heap(8192, m_uniform_buffer_offset_align);
	buf = static_cast<u8*>(mapping.first);
	vertex_constants_offset = mapping.second;
	fill_vertex_program_constants_data(buf);

	// Fragment constants
	mapping = m_uniform_ring_buffer->alloc_from_heap(fragment_buffer_size, m_uniform_buffer_offset_align);
	buf = static_cast<u8*>(mapping.first);
	fragment_constants_offset = mapping.second;
	if (fragment_constants_size)
		m_prog_buffer.fill_fragment_constants_buffer({ reinterpret_cast<float*>(buf), gsl::narrow<int>(fragment_constants_size) }, fragment_program);
	
	// Fragment state
	fill_fragment_state_buffer(buf+fragment_constants_size, fragment_program);

	m_uniform_ring_buffer->bind_range(0, scale_offset_offset, 512);
	m_uniform_ring_buffer->bind_range(1, vertex_constants_offset, 8192);
	m_uniform_ring_buffer->bind_range(2, fragment_constants_offset, fragment_buffer_size);

	if (manually_flush_ring_buffers)
		m_uniform_ring_buffer->unmap();

	return true;
}