Serenity Operating System
at master 1694 lines 72 kB view raw
1/* 2 * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org> 3 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com> 4 * Copyright (c) 2022-2023, Jelle Raaijmakers <jelle@gmta.nl> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <AK/AnyOf.h> 10#include <AK/Error.h> 11#include <AK/Math.h> 12#include <AK/NumericLimits.h> 13#include <AK/SIMDExtras.h> 14#include <AK/SIMDMath.h> 15#include <AK/String.h> 16#include <LibCore/ElapsedTimer.h> 17#include <LibGfx/Painter.h> 18#include <LibGfx/Vector2.h> 19#include <LibGfx/Vector3.h> 20#include <LibSoftGPU/Config.h> 21#include <LibSoftGPU/Device.h> 22#include <LibSoftGPU/Image.h> 23#include <LibSoftGPU/PixelConverter.h> 24#include <LibSoftGPU/PixelQuad.h> 25#include <LibSoftGPU/SIMD.h> 26#include <LibSoftGPU/Shader.h> 27#include <LibSoftGPU/ShaderCompiler.h> 28#include <math.h> 29 30namespace SoftGPU { 31 32static i64 g_num_rasterized_triangles; 33static i64 g_num_pixels; 34static i64 g_num_pixels_shaded; 35static i64 g_num_pixels_blended; 36static i64 g_num_sampler_calls; 37static i64 g_num_stencil_writes; 38static i64 g_num_quads; 39 40using AK::abs; 41using AK::SIMD::any; 42using AK::SIMD::exp_approximate; 43using AK::SIMD::expand4; 44using AK::SIMD::f32x4; 45using AK::SIMD::i32x4; 46using AK::SIMD::load4_masked; 47using AK::SIMD::maskbits; 48using AK::SIMD::maskcount; 49using AK::SIMD::store4_masked; 50using AK::SIMD::to_f32x4; 51using AK::SIMD::to_u32x4; 52using AK::SIMD::u32x4; 53 54static constexpr int subpixel_factor = 1 << SUBPIXEL_BITS; 55 56// Returns positive values for counter-clockwise rotation of vertices. Note that it returns the 57// area of a parallelogram with sides {a, b} and {b, c}, so _double_ the area of the triangle {a, b, c}. 58constexpr static i32 edge_function(IntVector2 const& a, IntVector2 const& b, IntVector2 const& c) 59{ 60 return (c.y() - a.y()) * (b.x() - a.x()) - (c.x() - a.x()) * (b.y() - a.y()); 61} 62 63constexpr static i32x4 edge_function4(IntVector2 const& a, IntVector2 const& b, Vector2<i32x4> const& c) 64{ 65 return (c.y() - a.y()) * (b.x() - a.x()) - (c.x() - a.x()) * (b.y() - a.y()); 66} 67 68template<typename T, typename U> 69constexpr static auto interpolate(T const& v0, T const& v1, T const& v2, Vector3<U> const& barycentric_coords) 70{ 71 return v0 * barycentric_coords.x() + v1 * barycentric_coords.y() + v2 * barycentric_coords.z(); 72} 73 74static GPU::ColorType to_argb32(FloatVector4 const& color) 75{ 76 auto clamped = color.clamped(0.0f, 1.0f); 77 auto r = static_cast<u8>(clamped.x() * 255); 78 auto g = static_cast<u8>(clamped.y() * 255); 79 auto b = static_cast<u8>(clamped.z() * 255); 80 auto a = static_cast<u8>(clamped.w() * 255); 81 return a << 24 | r << 16 | g << 8 | b; 82} 83 84ALWAYS_INLINE static u32x4 to_argb32(Vector4<f32x4> const& color) 85{ 86 auto clamped = color.clamped(expand4(0.0f), expand4(1.0f)); 87 auto r = to_u32x4(clamped.x() * 255); 88 auto g = to_u32x4(clamped.y() * 255); 89 auto b = to_u32x4(clamped.z() * 255); 90 auto a = to_u32x4(clamped.w() * 255); 91 92 return a << 24 | r << 16 | g << 8 | b; 93} 94 95static Vector4<f32x4> to_vec4(u32x4 bgra) 96{ 97 auto constexpr one_over_255 = expand4(1.0f / 255); 98 return { 99 to_f32x4((bgra >> 16) & 0xff) * one_over_255, 100 to_f32x4((bgra >> 8) & 0xff) * one_over_255, 101 to_f32x4(bgra & 0xff) * one_over_255, 102 to_f32x4((bgra >> 24) & 0xff) * one_over_255, 103 }; 104} 105 106ALWAYS_INLINE static void test_alpha(PixelQuad& quad, GPU::AlphaTestFunction alpha_test_function, f32x4 const& reference_value) 107{ 108 auto const alpha = quad.get_output_float(SHADER_OUTPUT_FIRST_COLOR + 3); 109 110 switch (alpha_test_function) { 111 case GPU::AlphaTestFunction::Always: 112 quad.mask &= expand4(~0); 113 break; 114 case GPU::AlphaTestFunction::Equal: 115 quad.mask &= alpha == reference_value; 116 break; 117 case GPU::AlphaTestFunction::Greater: 118 quad.mask &= alpha > reference_value; 119 break; 120 case GPU::AlphaTestFunction::GreaterOrEqual: 121 quad.mask &= alpha >= reference_value; 122 break; 123 case GPU::AlphaTestFunction::Less: 124 quad.mask &= alpha < reference_value; 125 break; 126 case GPU::AlphaTestFunction::LessOrEqual: 127 quad.mask &= alpha <= reference_value; 128 break; 129 case GPU::AlphaTestFunction::NotEqual: 130 quad.mask &= alpha != reference_value; 131 break; 132 case GPU::AlphaTestFunction::Never: 133 default: 134 VERIFY_NOT_REACHED(); 135 } 136} 137 138ALWAYS_INLINE static bool is_blend_factor_constant(GPU::BlendFactor blend_factor) 139{ 140 return (blend_factor == GPU::BlendFactor::One || blend_factor == GPU::BlendFactor::Zero); 141} 142 143// OpenGL 1.5 § 4.1.8, table 4.1 144ALWAYS_INLINE static Vector4<f32x4> get_blend_factor(GPU::BlendFactor blend_factor, Vector4<f32x4> const& source_color, Vector4<f32x4> const& destination_color) 145{ 146 switch (blend_factor) { 147 case GPU::BlendFactor::DstAlpha: 148 return to_vec4(destination_color.w()); 149 case GPU::BlendFactor::DstColor: 150 return destination_color; 151 case GPU::BlendFactor::One: 152 return to_vec4(expand4(1.f)); 153 case GPU::BlendFactor::OneMinusDstAlpha: 154 return to_vec4(1.f - destination_color.w()); 155 case GPU::BlendFactor::OneMinusDstColor: 156 return to_vec4(expand4(1.f)) - destination_color; 157 case GPU::BlendFactor::OneMinusSrcAlpha: 158 return to_vec4(1.f - source_color.w()); 159 case GPU::BlendFactor::OneMinusSrcColor: 160 return to_vec4(expand4(1.f)) - source_color; 161 case GPU::BlendFactor::SrcAlpha: 162 return to_vec4(source_color.w()); 163 case GPU::BlendFactor::SrcAlphaSaturate: { 164 auto saturated = min(source_color.w(), 1.f - destination_color.w()); 165 return { saturated, saturated, saturated, expand4(1.f) }; 166 } 167 case GPU::BlendFactor::SrcColor: 168 return source_color; 169 case GPU::BlendFactor::Zero: 170 return to_vec4(expand4(0.f)); 171 default: 172 VERIFY_NOT_REACHED(); 173 } 174} 175 176template<typename CB1, typename CB2, typename CB3> 177ALWAYS_INLINE void Device::rasterize(Gfx::IntRect& render_bounds, CB1 set_coverage_mask, CB2 set_quad_depth, CB3 set_quad_attributes) 178{ 179 // Return if alpha testing is a no-op 180 if (m_options.enable_alpha_test && m_options.alpha_test_func == GPU::AlphaTestFunction::Never) 181 return; 182 auto const alpha_test_ref_value = expand4(m_options.alpha_test_ref_value); 183 184 // Buffers 185 auto color_buffer = m_frame_buffer->color_buffer(); 186 auto depth_buffer = m_frame_buffer->depth_buffer(); 187 auto stencil_buffer = m_frame_buffer->stencil_buffer(); 188 189 // Stencil configuration and writing 190 auto const& stencil_configuration = m_stencil_configuration[GPU::Face::Front]; 191 auto const stencil_reference_value = stencil_configuration.reference_value & stencil_configuration.test_mask; 192 193 auto write_to_stencil = [](GPU::StencilType* stencil_ptrs[4], i32x4 stencil_value, GPU::StencilOperation op, GPU::StencilType reference_value, GPU::StencilType write_mask, i32x4 pixel_mask) { 194 if (write_mask == 0 || op == GPU::StencilOperation::Keep) 195 return; 196 197 switch (op) { 198 case GPU::StencilOperation::Decrement: 199 stencil_value = (stencil_value & ~write_mask) | (max(stencil_value - 1, expand4(0)) & write_mask); 200 break; 201 case GPU::StencilOperation::DecrementWrap: 202 stencil_value = (stencil_value & ~write_mask) | (((stencil_value - 1) & 0xFF) & write_mask); 203 break; 204 case GPU::StencilOperation::Increment: 205 stencil_value = (stencil_value & ~write_mask) | (min(stencil_value + 1, expand4(0xFF)) & write_mask); 206 break; 207 case GPU::StencilOperation::IncrementWrap: 208 stencil_value = (stencil_value & ~write_mask) | (((stencil_value + 1) & 0xFF) & write_mask); 209 break; 210 case GPU::StencilOperation::Invert: 211 stencil_value ^= write_mask; 212 break; 213 case GPU::StencilOperation::Replace: 214 stencil_value = (stencil_value & ~write_mask) | (reference_value & write_mask); 215 break; 216 case GPU::StencilOperation::Zero: 217 stencil_value &= ~write_mask; 218 break; 219 default: 220 VERIFY_NOT_REACHED(); 221 } 222 223 INCREASE_STATISTICS_COUNTER(g_num_stencil_writes, maskcount(pixel_mask)); 224 store4_masked(stencil_value, stencil_ptrs[0], stencil_ptrs[1], stencil_ptrs[2], stencil_ptrs[3], pixel_mask); 225 }; 226 227 // Limit rendering to framebuffer and scissor rects 228 render_bounds.intersect(m_frame_buffer->rect()); 229 if (m_options.scissor_enabled) 230 render_bounds.intersect(m_options.scissor_box); 231 232 // Quad bounds 233 auto const render_bounds_left = render_bounds.left(); 234 auto const render_bounds_right = render_bounds.right(); 235 auto const render_bounds_top = render_bounds.top(); 236 auto const render_bounds_bottom = render_bounds.bottom(); 237 auto const qx0 = render_bounds_left & ~1; 238 auto const qx1 = render_bounds_right & ~1; 239 auto const qy0 = render_bounds_top & ~1; 240 auto const qy1 = render_bounds_bottom & ~1; 241 242 // Blend factors 243 Vector4<f32x4> src_factor; 244 Vector4<f32x4> dst_factor; 245 auto const src_factor_is_constant = is_blend_factor_constant(m_options.blend_source_factor); 246 auto const dst_factor_is_constant = is_blend_factor_constant(m_options.blend_destination_factor); 247 if (m_options.enable_blending) { 248 if (src_factor_is_constant) 249 src_factor = get_blend_factor(m_options.blend_source_factor, {}, {}); 250 if (dst_factor_is_constant) 251 dst_factor = get_blend_factor(m_options.blend_destination_factor, {}, {}); 252 } 253 254 // Rasterize all quads 255 // FIXME: this could be embarrassingly parallel 256 for (int qy = qy0; qy <= qy1; qy += 2) { 257 for (int qx = qx0; qx <= qx1; qx += 2) { 258 PixelQuad quad; 259 quad.screen_coordinates = { 260 i32x4 { qx, qx + 1, qx, qx + 1 }, 261 i32x4 { qy, qy, qy + 1, qy + 1 }, 262 }; 263 264 // Set coverage mask and test against render bounds 265 set_coverage_mask(quad); 266 quad.mask &= quad.screen_coordinates.x() >= render_bounds_left 267 && quad.screen_coordinates.x() <= render_bounds_right 268 && quad.screen_coordinates.y() >= render_bounds_top 269 && quad.screen_coordinates.y() <= render_bounds_bottom; 270 auto coverage_bits = maskbits(quad.mask); 271 if (coverage_bits == 0) 272 continue; 273 274 INCREASE_STATISTICS_COUNTER(g_num_quads, 1); 275 INCREASE_STATISTICS_COUNTER(g_num_pixels, maskcount(quad.mask)); 276 277 // Stencil testing 278 GPU::StencilType* stencil_ptrs[4]; 279 i32x4 stencil_value; 280 if (m_options.enable_stencil_test) { 281 stencil_ptrs[0] = coverage_bits & 1 ? &stencil_buffer->scanline(qy)[qx] : nullptr; 282 stencil_ptrs[1] = coverage_bits & 2 ? &stencil_buffer->scanline(qy)[qx + 1] : nullptr; 283 stencil_ptrs[2] = coverage_bits & 4 ? &stencil_buffer->scanline(qy + 1)[qx] : nullptr; 284 stencil_ptrs[3] = coverage_bits & 8 ? &stencil_buffer->scanline(qy + 1)[qx + 1] : nullptr; 285 286 stencil_value = load4_masked(stencil_ptrs[0], stencil_ptrs[1], stencil_ptrs[2], stencil_ptrs[3], quad.mask); 287 stencil_value &= stencil_configuration.test_mask; 288 289 i32x4 stencil_test_passed; 290 switch (stencil_configuration.test_function) { 291 case GPU::StencilTestFunction::Always: 292 stencil_test_passed = expand4(~0); 293 break; 294 case GPU::StencilTestFunction::Equal: 295 stencil_test_passed = stencil_value == stencil_reference_value; 296 break; 297 case GPU::StencilTestFunction::Greater: 298 stencil_test_passed = stencil_value > stencil_reference_value; 299 break; 300 case GPU::StencilTestFunction::GreaterOrEqual: 301 stencil_test_passed = stencil_value >= stencil_reference_value; 302 break; 303 case GPU::StencilTestFunction::Less: 304 stencil_test_passed = stencil_value < stencil_reference_value; 305 break; 306 case GPU::StencilTestFunction::LessOrEqual: 307 stencil_test_passed = stencil_value <= stencil_reference_value; 308 break; 309 case GPU::StencilTestFunction::Never: 310 stencil_test_passed = expand4(0); 311 break; 312 case GPU::StencilTestFunction::NotEqual: 313 stencil_test_passed = stencil_value != stencil_reference_value; 314 break; 315 default: 316 VERIFY_NOT_REACHED(); 317 } 318 319 // Update stencil buffer for pixels that failed the stencil test 320 write_to_stencil( 321 stencil_ptrs, 322 stencil_value, 323 stencil_configuration.on_stencil_test_fail, 324 stencil_reference_value, 325 stencil_configuration.write_mask, 326 quad.mask & ~stencil_test_passed); 327 328 // Update coverage mask + early quad rejection 329 quad.mask &= stencil_test_passed; 330 coverage_bits = maskbits(quad.mask); 331 if (coverage_bits == 0) 332 continue; 333 } 334 335 // Depth testing 336 GPU::DepthType* depth_ptrs[4] = { 337 coverage_bits & 1 ? &depth_buffer->scanline(qy)[qx] : nullptr, 338 coverage_bits & 2 ? &depth_buffer->scanline(qy)[qx + 1] : nullptr, 339 coverage_bits & 4 ? &depth_buffer->scanline(qy + 1)[qx] : nullptr, 340 coverage_bits & 8 ? &depth_buffer->scanline(qy + 1)[qx + 1] : nullptr, 341 }; 342 if (m_options.enable_depth_test) { 343 set_quad_depth(quad); 344 345 auto depth = load4_masked(depth_ptrs[0], depth_ptrs[1], depth_ptrs[2], depth_ptrs[3], quad.mask); 346 i32x4 depth_test_passed; 347 switch (m_options.depth_func) { 348 case GPU::DepthTestFunction::Always: 349 depth_test_passed = expand4(~0); 350 break; 351 case GPU::DepthTestFunction::Never: 352 depth_test_passed = expand4(0); 353 break; 354 case GPU::DepthTestFunction::Greater: 355 depth_test_passed = quad.depth > depth; 356 break; 357 case GPU::DepthTestFunction::GreaterOrEqual: 358 depth_test_passed = quad.depth >= depth; 359 break; 360 case GPU::DepthTestFunction::NotEqual: 361 depth_test_passed = quad.depth != depth; 362 break; 363 case GPU::DepthTestFunction::Equal: 364 depth_test_passed = quad.depth == depth; 365 break; 366 case GPU::DepthTestFunction::LessOrEqual: 367 depth_test_passed = quad.depth <= depth; 368 break; 369 case GPU::DepthTestFunction::Less: 370 depth_test_passed = quad.depth < depth; 371 break; 372 default: 373 VERIFY_NOT_REACHED(); 374 } 375 376 // Update stencil buffer for pixels that failed the depth test 377 if (m_options.enable_stencil_test) { 378 write_to_stencil( 379 stencil_ptrs, 380 stencil_value, 381 stencil_configuration.on_depth_test_fail, 382 stencil_reference_value, 383 stencil_configuration.write_mask, 384 quad.mask & ~depth_test_passed); 385 } 386 387 // Update coverage mask + early quad rejection 388 quad.mask &= depth_test_passed; 389 coverage_bits = maskbits(quad.mask); 390 if (coverage_bits == 0) 391 continue; 392 } 393 394 // Update stencil buffer for passed pixels 395 if (m_options.enable_stencil_test) { 396 write_to_stencil( 397 stencil_ptrs, 398 stencil_value, 399 stencil_configuration.on_pass, 400 stencil_reference_value, 401 stencil_configuration.write_mask, 402 quad.mask); 403 } 404 405 INCREASE_STATISTICS_COUNTER(g_num_pixels_shaded, maskcount(quad.mask)); 406 407 set_quad_attributes(quad); 408 shade_fragments(quad); 409 410 // Alpha testing 411 if (m_options.enable_alpha_test) { 412 test_alpha(quad, m_options.alpha_test_func, alpha_test_ref_value); 413 coverage_bits = maskbits(quad.mask); 414 if (coverage_bits == 0) 415 continue; 416 } 417 418 // Write to depth buffer 419 if (m_options.enable_depth_test && m_options.enable_depth_write) 420 store4_masked(quad.depth, depth_ptrs[0], depth_ptrs[1], depth_ptrs[2], depth_ptrs[3], quad.mask); 421 422 // We will not update the color buffer at all 423 if ((m_options.color_mask == 0) || !m_options.enable_color_write) 424 continue; 425 426 GPU::ColorType* color_ptrs[4] = { 427 coverage_bits & 1 ? &color_buffer->scanline(qy)[qx] : nullptr, 428 coverage_bits & 2 ? &color_buffer->scanline(qy)[qx + 1] : nullptr, 429 coverage_bits & 4 ? &color_buffer->scanline(qy + 1)[qx] : nullptr, 430 coverage_bits & 8 ? &color_buffer->scanline(qy + 1)[qx + 1] : nullptr, 431 }; 432 433 u32x4 dst_u32; 434 if (m_options.enable_blending || m_options.color_mask != 0xffffffff) 435 dst_u32 = load4_masked(color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); 436 437 auto out_color = quad.get_output_vector4(SHADER_OUTPUT_FIRST_COLOR); 438 439 if (m_options.enable_blending) { 440 INCREASE_STATISTICS_COUNTER(g_num_pixels_blended, maskcount(quad.mask)); 441 442 // Blend color values from pixel_staging into color_buffer 443 auto const& src = out_color; 444 auto const dst = to_vec4(dst_u32); 445 446 if (!src_factor_is_constant) 447 src_factor = get_blend_factor(m_options.blend_source_factor, src, dst); 448 if (!dst_factor_is_constant) 449 dst_factor = get_blend_factor(m_options.blend_destination_factor, src, dst); 450 451 out_color = src * src_factor + dst * dst_factor; 452 } 453 454 auto const argb32_color = to_argb32(out_color); 455 if (m_options.color_mask == 0xffffffff) 456 store4_masked(argb32_color, color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); 457 else 458 store4_masked((argb32_color & m_options.color_mask) | (dst_u32 & ~m_options.color_mask), color_ptrs[0], color_ptrs[1], color_ptrs[2], color_ptrs[3], quad.mask); 459 } 460 } 461} 462 463void Device::rasterize_line_aliased(GPU::Vertex& from, GPU::Vertex& to) 464{ 465 // FIXME: implement aliased lines; for now we fall back to anti-aliased logic 466 rasterize_line_antialiased(from, to); 467} 468 469void Device::rasterize_line_antialiased(GPU::Vertex& from, GPU::Vertex& to) 470{ 471 auto const from_coords = from.window_coordinates.xy(); 472 auto const to_coords = to.window_coordinates.xy(); 473 auto const line_width = ceilf(m_options.line_width); 474 auto const line_radius = line_width / 2; 475 476 auto render_bounds = Gfx::IntRect { 477 min(from_coords.x(), to_coords.x()), 478 min(from_coords.y(), to_coords.y()), 479 abs(from_coords.x() - to_coords.x()) + 1, 480 abs(from_coords.y() - to_coords.y()) + 1, 481 }; 482 render_bounds.inflate(line_width, line_width); 483 484 auto const from_coords4 = expand4(from_coords); 485 auto const line_vector = to_coords - from_coords; 486 auto const line_vector4 = expand4(line_vector); 487 auto const line_dot4 = expand4(line_vector.dot(line_vector)); 488 489 auto const from_depth4 = expand4(from.window_coordinates.z()); 490 auto const to_depth4 = expand4(to.window_coordinates.z()); 491 492 auto const from_color4 = expand4(from.color); 493 auto const from_fog_depth4 = expand4(abs(from.eye_coordinates.z())); 494 495 // Rasterize using a 2D signed distance field for a line segment 496 // FIXME: performance-wise, this might be the absolute worst way to draw an anti-aliased line 497 f32x4 distance_along_line; 498 rasterize( 499 render_bounds, 500 [&from_coords4, &distance_along_line, &line_vector4, &line_dot4, &line_radius](auto& quad) { 501 auto const screen_coordinates4 = to_vec2_f32x4(quad.screen_coordinates); 502 auto const pixel_vector = screen_coordinates4 - from_coords4; 503 distance_along_line = AK::SIMD::clamp(pixel_vector.dot(line_vector4) / line_dot4, 0.f, 1.f); 504 auto distance_to_line = length(pixel_vector - line_vector4 * distance_along_line) - line_radius; 505 506 // Add .5f to the distance so coverage transitions half a pixel before the actual border 507 quad.coverage = 1.f - AK::SIMD::clamp(distance_to_line + 0.5f, 0.f, 1.f); 508 quad.mask = quad.coverage > 0.f; 509 }, 510 [&from_depth4, &to_depth4, &distance_along_line](auto& quad) { 511 quad.depth = mix(from_depth4, to_depth4, distance_along_line); 512 }, 513 [&from_color4, &from, &from_fog_depth4](auto& quad) { 514 // FIXME: interpolate color, tex coords and fog depth along the distance of the line 515 // in clip space (i.e. NOT distance_from_line) 516 quad.set_input(SHADER_INPUT_VERTEX_COLOR, from_color4); 517 for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) 518 quad.set_input(SHADER_INPUT_FIRST_TEXCOORD + i * 4, expand4(from.tex_coords[i])); 519 520 quad.fog_depth = from_fog_depth4; 521 }); 522} 523 524void Device::rasterize_line(GPU::Vertex& from, GPU::Vertex& to) 525{ 526 if (m_options.line_smooth) 527 rasterize_line_antialiased(from, to); 528 else 529 rasterize_line_aliased(from, to); 530} 531 532void Device::rasterize_point_aliased(GPU::Vertex& point) 533{ 534 // Determine aliased point width 535 constexpr size_t maximum_aliased_point_size = 64; 536 auto point_width = clamp(round_to<int>(m_options.point_size), 1, maximum_aliased_point_size); 537 538 // Determine aliased center coordinates 539 IntVector2 point_center; 540 if (point_width % 2 == 1) 541 point_center = point.window_coordinates.xy().to_type<int>(); 542 else 543 point_center = (point.window_coordinates.xy() + FloatVector2 { .5f, .5f }).to_type<int>(); 544 545 // Aliased points are rects; calculate boundaries around center 546 auto point_rect = Gfx::IntRect { 547 point_center.x() - point_width / 2, 548 point_center.y() - point_width / 2, 549 point_width, 550 point_width, 551 }; 552 553 // Rasterize the point as a rect 554 rasterize( 555 point_rect, 556 [](auto& quad) { 557 // We already passed in point_rect, so this doesn't matter 558 quad.mask = expand4(~0); 559 }, 560 [&point](auto& quad) { 561 quad.depth = expand4(point.window_coordinates.z()); 562 }, 563 [&point](auto& quad) { 564 quad.set_input(SHADER_INPUT_VERTEX_COLOR, expand4(point.color)); 565 for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) 566 quad.set_input(SHADER_INPUT_FIRST_TEXCOORD + i * 4, expand4(point.tex_coords[i])); 567 568 quad.fog_depth = expand4(abs(point.eye_coordinates.z())); 569 }); 570} 571 572void Device::rasterize_point_antialiased(GPU::Vertex& point) 573{ 574 auto const center = point.window_coordinates.xy(); 575 auto const center4 = expand4(center); 576 auto const radius = m_options.point_size / 2; 577 578 auto render_bounds = Gfx::IntRect { 579 center.x() - radius, 580 center.y() - radius, 581 radius * 2 + 1, 582 radius * 2 + 1, 583 }; 584 585 // Rasterize using a 2D signed distance field for a circle 586 rasterize( 587 render_bounds, 588 [&center4, &radius](auto& quad) { 589 auto screen_coords = to_vec2_f32x4(quad.screen_coordinates); 590 auto distance_to_point = length(center4 - screen_coords) - radius; 591 592 // Add .5f to the distance so coverage transitions half a pixel before the actual border 593 quad.coverage = 1.f - AK::SIMD::clamp(distance_to_point + .5f, 0.f, 1.f); 594 quad.mask = quad.coverage > 0.f; 595 }, 596 [&point](auto& quad) { 597 quad.depth = expand4(point.window_coordinates.z()); 598 }, 599 [&point](auto& quad) { 600 quad.set_input(SHADER_INPUT_VERTEX_COLOR, expand4(point.color)); 601 for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) 602 quad.set_input(SHADER_INPUT_FIRST_TEXCOORD + i * 4, expand4(point.tex_coords[i])); 603 604 quad.fog_depth = expand4(abs(point.eye_coordinates.z())); 605 }); 606} 607 608void Device::rasterize_point(GPU::Vertex& point) 609{ 610 if (m_options.point_smooth) 611 rasterize_point_antialiased(point); 612 else 613 rasterize_point_aliased(point); 614} 615 616void Device::rasterize_triangle(Triangle& triangle) 617{ 618 INCREASE_STATISTICS_COUNTER(g_num_rasterized_triangles, 1); 619 620 auto v0 = (triangle.vertices[0].window_coordinates.xy() * subpixel_factor).to_rounded<int>(); 621 auto v1 = (triangle.vertices[1].window_coordinates.xy() * subpixel_factor).to_rounded<int>(); 622 auto v2 = (triangle.vertices[2].window_coordinates.xy() * subpixel_factor).to_rounded<int>(); 623 624 auto triangle_area = edge_function(v0, v1, v2); 625 if (triangle_area == 0) 626 return; 627 628 // Perform face culling 629 if (m_options.enable_culling) { 630 bool is_front = (m_options.front_face == GPU::WindingOrder::CounterClockwise ? triangle_area > 0 : triangle_area < 0); 631 632 if (!is_front && m_options.cull_back) 633 return; 634 635 if (is_front && m_options.cull_front) 636 return; 637 } 638 639 // Force counter-clockwise ordering of vertices 640 if (triangle_area < 0) { 641 swap(triangle.vertices[0], triangle.vertices[1]); 642 swap(v0, v1); 643 triangle_area *= -1; 644 } 645 646 auto const& vertex0 = triangle.vertices[0]; 647 auto const& vertex1 = triangle.vertices[1]; 648 auto const& vertex2 = triangle.vertices[2]; 649 650 auto const one_over_area = 1.0f / triangle_area; 651 652 // This function calculates the 3 edge values for the pixel relative to the triangle. 653 auto calculate_edge_values4 = [v0, v1, v2](Vector2<i32x4> const& p) -> Vector3<i32x4> { 654 return { 655 edge_function4(v1, v2, p), 656 edge_function4(v2, v0, p), 657 edge_function4(v0, v1, p), 658 }; 659 }; 660 661 // Zero is used in testing against edge values below, applying the "top-left rule". If a pixel 662 // lies exactly on an edge shared by two triangles, we only render that pixel if the edge in 663 // question is a "top" or "left" edge. By setting either a 1 or 0, we effectively change the 664 // comparisons against the edge values below from "> 0" into ">= 0". 665 IntVector3 const zero { 666 (v2.y() < v1.y() || (v2.y() == v1.y() && v2.x() < v1.x())) ? 0 : 1, 667 (v0.y() < v2.y() || (v0.y() == v2.y() && v0.x() < v2.x())) ? 0 : 1, 668 (v1.y() < v0.y() || (v1.y() == v0.y() && v1.x() < v0.x())) ? 0 : 1, 669 }; 670 671 // This function tests whether a point as identified by its 3 edge values lies within the triangle 672 auto test_point4 = [zero](Vector3<i32x4> const& edges) -> i32x4 { 673 return edges.x() >= zero.x() 674 && edges.y() >= zero.y() 675 && edges.z() >= zero.z(); 676 }; 677 678 // Calculate render bounds based on the triangle's vertices 679 Gfx::IntRect render_bounds; 680 render_bounds.set_left(min(min(v0.x(), v1.x()), v2.x()) / subpixel_factor); 681 render_bounds.set_right(max(max(v0.x(), v1.x()), v2.x()) / subpixel_factor); 682 render_bounds.set_top(min(min(v0.y(), v1.y()), v2.y()) / subpixel_factor); 683 render_bounds.set_bottom(max(max(v0.y(), v1.y()), v2.y()) / subpixel_factor); 684 685 // Calculate depth of fragment for fog; 686 // OpenGL 1.5 chapter 3.10: "An implementation may choose to approximate the 687 // eye-coordinate distance from the eye to each fragment center by |Ze|." 688 Vector3<f32x4> fog_depth; 689 if (m_options.fog_enabled) { 690 fog_depth = { 691 expand4(abs(vertex0.eye_coordinates.z())), 692 expand4(abs(vertex1.eye_coordinates.z())), 693 expand4(abs(vertex2.eye_coordinates.z())), 694 }; 695 } 696 697 auto const half_pixel_offset = Vector2<i32x4> { expand4(subpixel_factor / 2), expand4(subpixel_factor / 2) }; 698 699 auto const window_w_coordinates = Vector3<f32x4> { 700 expand4(vertex0.window_coordinates.w()), 701 expand4(vertex1.window_coordinates.w()), 702 expand4(vertex2.window_coordinates.w()), 703 }; 704 705 // Calculate depth offset to apply 706 float depth_offset = 0.f; 707 if (m_options.depth_offset_enabled) { 708 // OpenGL 2.0 § 3.5.5 allows us to approximate the maximum slope 709 auto delta_z = max( 710 max( 711 abs(vertex0.window_coordinates.z() - vertex1.window_coordinates.z()), 712 abs(vertex1.window_coordinates.z() - vertex2.window_coordinates.z())), 713 abs(vertex2.window_coordinates.z() - vertex0.window_coordinates.z())); 714 auto depth_max_slope = max(delta_z / render_bounds.width(), delta_z / render_bounds.height()); 715 716 // Calculate total depth offset 717 depth_offset = depth_max_slope * m_options.depth_offset_factor + NumericLimits<float>::epsilon() * m_options.depth_offset_constant; 718 } 719 720 auto const window_z_coordinates = Vector3<f32x4> { 721 expand4(vertex0.window_coordinates.z() + depth_offset), 722 expand4(vertex1.window_coordinates.z() + depth_offset), 723 expand4(vertex2.window_coordinates.z() + depth_offset), 724 }; 725 726 rasterize( 727 render_bounds, 728 [&](auto& quad) { 729 auto edge_values = calculate_edge_values4(quad.screen_coordinates * subpixel_factor + half_pixel_offset); 730 quad.mask = test_point4(edge_values); 731 732 quad.barycentrics = { 733 to_f32x4(edge_values.x()), 734 to_f32x4(edge_values.y()), 735 to_f32x4(edge_values.z()), 736 }; 737 }, 738 [&](auto& quad) { 739 // Determine each edge's ratio to the total area 740 quad.barycentrics = quad.barycentrics * one_over_area; 741 742 // Because the Z coordinates were divided by W, we can interpolate between them 743 quad.depth = AK::SIMD::clamp(window_z_coordinates.dot(quad.barycentrics), 0.f, 1.f); 744 }, 745 [&](auto& quad) { 746 auto const interpolated_reciprocal_w = window_w_coordinates.dot(quad.barycentrics); 747 quad.barycentrics = quad.barycentrics * window_w_coordinates / interpolated_reciprocal_w; 748 749 // FIXME: make this more generic. We want to interpolate more than just color and uv 750 if (m_options.shade_smooth) 751 quad.set_input(SHADER_INPUT_VERTEX_COLOR, interpolate(expand4(vertex0.color), expand4(vertex1.color), expand4(vertex2.color), quad.barycentrics)); 752 else 753 quad.set_input(SHADER_INPUT_VERTEX_COLOR, expand4(vertex0.color)); 754 755 for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) 756 quad.set_input(SHADER_INPUT_FIRST_TEXCOORD + i * 4, interpolate(expand4(vertex0.tex_coords[i]), expand4(vertex1.tex_coords[i]), expand4(vertex2.tex_coords[i]), quad.barycentrics)); 757 758 if (m_options.fog_enabled) 759 quad.fog_depth = fog_depth.dot(quad.barycentrics); 760 }); 761} 762 763Device::Device(Gfx::IntSize size) 764 : m_frame_buffer(FrameBuffer<GPU::ColorType, GPU::DepthType, GPU::StencilType>::try_create(size).release_value_but_fixme_should_propagate_errors()) 765 , m_shader_processor(m_samplers) 766{ 767 m_options.scissor_box = m_frame_buffer->rect(); 768 m_options.viewport = m_frame_buffer->rect(); 769} 770 771GPU::DeviceInfo Device::info() const 772{ 773 return { 774 .vendor_name = "SerenityOS", 775 .device_name = "SoftGPU", 776 .num_texture_units = GPU::NUM_TEXTURE_UNITS, 777 .num_lights = NUM_LIGHTS, 778 .max_clip_planes = MAX_CLIP_PLANES, 779 .max_texture_size = MAX_TEXTURE_SIZE, 780 .max_texture_lod_bias = MAX_TEXTURE_LOD_BIAS, 781 .stencil_bits = sizeof(GPU::StencilType) * 8, 782 .supports_npot_textures = true, 783 .supports_texture_clamp_to_edge = true, 784 .supports_texture_env_add = true, 785 }; 786} 787 788static void generate_texture_coordinates(GPU::Vertex const& vertex, FloatVector4& tex_coord, GPU::TextureUnitConfiguration const& texture_unit_configuration) 789{ 790 auto generate_coordinate = [&](size_t config_index) -> float { 791 auto const& tex_coord_generation = texture_unit_configuration.tex_coord_generation[config_index]; 792 switch (tex_coord_generation.mode) { 793 case GPU::TexCoordGenerationMode::ObjectLinear: { 794 auto coefficients = tex_coord_generation.coefficients; 795 return coefficients.dot(vertex.position); 796 } 797 case GPU::TexCoordGenerationMode::EyeLinear: { 798 auto coefficients = tex_coord_generation.coefficients; 799 return coefficients.dot(vertex.eye_coordinates); 800 } 801 case GPU::TexCoordGenerationMode::SphereMap: { 802 auto const eye_unit = vertex.eye_coordinates.normalized(); 803 FloatVector3 const eye_unit_xyz = eye_unit.xyz(); 804 auto const normal = vertex.normal; 805 auto reflection = eye_unit_xyz - normal * 2 * normal.dot(eye_unit_xyz); 806 reflection.set_z(reflection.z() + 1); 807 auto const reflection_value = reflection[config_index]; 808 return reflection_value / (2 * reflection.length()) + 0.5f; 809 } 810 case GPU::TexCoordGenerationMode::ReflectionMap: { 811 auto const eye_unit = vertex.eye_coordinates.normalized(); 812 FloatVector3 const eye_unit_xyz = eye_unit.xyz(); 813 auto const normal = vertex.normal; 814 auto reflection = eye_unit_xyz - normal * 2 * normal.dot(eye_unit_xyz); 815 return reflection[config_index]; 816 } 817 case GPU::TexCoordGenerationMode::NormalMap: { 818 return vertex.normal[config_index]; 819 } 820 } 821 VERIFY_NOT_REACHED(); 822 }; 823 824 auto const enabled_coords = texture_unit_configuration.tex_coord_generation_enabled; 825 if (enabled_coords == GPU::TexCoordGenerationCoordinate::None) 826 return; 827 828 tex_coord = { 829 ((enabled_coords & GPU::TexCoordGenerationCoordinate::S) > 0) ? generate_coordinate(0) : tex_coord.x(), 830 ((enabled_coords & GPU::TexCoordGenerationCoordinate::T) > 0) ? generate_coordinate(1) : tex_coord.y(), 831 ((enabled_coords & GPU::TexCoordGenerationCoordinate::R) > 0) ? generate_coordinate(2) : tex_coord.z(), 832 ((enabled_coords & GPU::TexCoordGenerationCoordinate::Q) > 0) ? generate_coordinate(3) : tex_coord.w(), 833 }; 834} 835 836void Device::calculate_vertex_lighting(GPU::Vertex& vertex) const 837{ 838 if (!m_options.lighting_enabled) 839 return; 840 841 auto const& material = m_materials.at(0); 842 auto ambient = material.ambient; 843 auto diffuse = material.diffuse; 844 auto emissive = material.emissive; 845 auto specular = material.specular; 846 847 if (m_options.color_material_enabled 848 && (m_options.color_material_face == GPU::ColorMaterialFace::Front || m_options.color_material_face == GPU::ColorMaterialFace::FrontAndBack)) { 849 switch (m_options.color_material_mode) { 850 case GPU::ColorMaterialMode::Ambient: 851 ambient = vertex.color; 852 break; 853 case GPU::ColorMaterialMode::AmbientAndDiffuse: 854 ambient = vertex.color; 855 diffuse = vertex.color; 856 break; 857 case GPU::ColorMaterialMode::Diffuse: 858 diffuse = vertex.color; 859 break; 860 case GPU::ColorMaterialMode::Emissive: 861 emissive = vertex.color; 862 break; 863 case GPU::ColorMaterialMode::Specular: 864 specular = vertex.color; 865 break; 866 } 867 } 868 869 FloatVector4 result_color = emissive + ambient * m_lighting_model.scene_ambient_color; 870 871 for (auto const& light : m_lights) { 872 if (!light.is_enabled) 873 continue; 874 875 // We need to save the length here because the attenuation factor requires a non-normalized vector! 876 auto sgi_arrow_operator = [](FloatVector4 const& p1, FloatVector4 const& p2, float& output_length) { 877 FloatVector3 light_vector; 878 if ((p1.w() != 0.f) && (p2.w() == 0.f)) 879 light_vector = p2.xyz(); 880 else if ((p1.w() == 0.f) && (p2.w() != 0.f)) 881 light_vector = -p1.xyz(); 882 else 883 light_vector = p2.xyz() - p1.xyz(); 884 885 output_length = light_vector.length(); 886 if (output_length == 0.f) 887 return light_vector; 888 return light_vector / output_length; 889 }; 890 891 auto sgi_dot_operator = [](FloatVector3 const& d1, FloatVector3 const& d2) { 892 return AK::max(d1.dot(d2), 0.0f); 893 }; 894 895 float vertex_to_light_length = 0.f; 896 FloatVector3 vertex_to_light = sgi_arrow_operator(vertex.eye_coordinates, light.position, vertex_to_light_length); 897 898 // Light attenuation value. 899 float light_attenuation_factor = 1.0f; 900 if (light.position.w() != 0.0f) 901 light_attenuation_factor = 1.0f / (light.constant_attenuation + (light.linear_attenuation * vertex_to_light_length) + (light.quadratic_attenuation * vertex_to_light_length * vertex_to_light_length)); 902 903 // Spotlight factor 904 float spotlight_factor = 1.0f; 905 if (light.spotlight_cutoff_angle != 180.0f) { 906 auto const vertex_to_light_dot_spotlight_direction = sgi_dot_operator(vertex_to_light, light.spotlight_direction.normalized()); 907 auto const cos_spotlight_cutoff = AK::cos<float>(light.spotlight_cutoff_angle * AK::Pi<float> / 180.f); 908 909 if (vertex_to_light_dot_spotlight_direction >= cos_spotlight_cutoff) 910 spotlight_factor = AK::pow<float>(vertex_to_light_dot_spotlight_direction, light.spotlight_exponent); 911 else 912 spotlight_factor = 0.0f; 913 } 914 915 // FIXME: The spec allows for splitting the colors calculated here into multiple different colors (primary/secondary color). Investigate what this means. 916 (void)m_lighting_model.color_control; 917 918 // FIXME: Two sided lighting should be implemented eventually (I believe this is where the normals are -ve and then lighting is calculated with the BACK material) 919 (void)m_lighting_model.two_sided_lighting; 920 921 // Ambient 922 auto const ambient_component = ambient * light.ambient_intensity; 923 924 // Diffuse 925 auto const normal_dot_vertex_to_light = sgi_dot_operator(vertex.normal, vertex_to_light); 926 auto const diffuse_component = diffuse * light.diffuse_intensity * normal_dot_vertex_to_light; 927 928 // Specular 929 FloatVector4 specular_component = { 0.0f, 0.0f, 0.0f, 0.0f }; 930 if (normal_dot_vertex_to_light > 0.0f) { 931 FloatVector3 half_vector_normalized; 932 if (!m_lighting_model.viewer_at_infinity) { 933 half_vector_normalized = vertex_to_light + FloatVector3(0.0f, 0.0f, 1.0f); 934 } else { 935 auto const vertex_to_eye_point = sgi_arrow_operator(vertex.eye_coordinates, { 0.f, 0.f, 0.f, 1.f }, vertex_to_light_length); 936 half_vector_normalized = vertex_to_light + vertex_to_eye_point; 937 } 938 half_vector_normalized.normalize(); 939 940 auto const normal_dot_half_vector = sgi_dot_operator(vertex.normal, half_vector_normalized); 941 auto const specular_coefficient = AK::pow(normal_dot_half_vector, material.shininess); 942 specular_component = specular * light.specular_intensity * specular_coefficient; 943 } 944 945 auto color = ambient_component + diffuse_component + specular_component; 946 color = color * light_attenuation_factor * spotlight_factor; 947 result_color += color; 948 } 949 950 vertex.color = result_color; 951 vertex.color.set_w(diffuse.w()); // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material" 952 vertex.color.clamp(0.0f, 1.0f); 953} 954 955void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, Vector<GPU::Vertex>& vertices) 956{ 957 // At this point, the user has effectively specified that they are done with defining the geometry 958 // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview): 959 // 960 // 1. Transform all of the vertices in the current vertex list into eye space by multiplying the model-view matrix 961 // 2. Transform all of the vertices from eye space into clip space by multiplying by the projection matrix 962 // 3. If culling is enabled, we cull the desired faces (https://learnopengl.com/Advanced-OpenGL/Face-culling) 963 // 4. Each element of the vertex is then divided by w to bring the positions into NDC (Normalized Device Coordinates) 964 // 5. The triangle's vertices are sorted in a counter-clockwise orientation 965 // 6. The triangles are then sent off to the rasterizer and drawn to the screen 966 967 if (vertices.is_empty()) 968 return; 969 970 // Set up normals transform by taking the upper left 3x3 elements from the model view matrix 971 // See section 2.11.3 of the OpenGL 1.5 spec 972 auto const normal_transform = model_view_transform.submatrix_from_topleft<3>().transpose().inverse(); 973 974 // First, transform all vertices 975 for (auto& vertex : vertices) { 976 vertex.eye_coordinates = model_view_transform * vertex.position; 977 978 vertex.normal = normal_transform * vertex.normal; 979 if (m_options.normalization_enabled) 980 vertex.normal.normalize(); 981 982 calculate_vertex_lighting(vertex); 983 984 vertex.clip_coordinates = projection_transform * vertex.eye_coordinates; 985 986 for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) { 987 auto const& texture_unit_configuration = m_texture_unit_configuration[i]; 988 if (!texture_unit_configuration.enabled) 989 continue; 990 generate_texture_coordinates(vertex, vertex.tex_coords[i], texture_unit_configuration); 991 vertex.tex_coords[i] = texture_unit_configuration.transformation_matrix * vertex.tex_coords[i]; 992 } 993 } 994 995 // Window coordinate calculation 996 auto const viewport = m_options.viewport; 997 auto const viewport_half_width = viewport.width() / 2.f; 998 auto const viewport_half_height = viewport.height() / 2.f; 999 auto const viewport_center_x = viewport.x() + viewport_half_width; 1000 auto const viewport_center_y = viewport.y() + viewport_half_height; 1001 auto const depth_half_range = (m_options.depth_max - m_options.depth_min) / 2; 1002 auto const depth_halfway = (m_options.depth_min + m_options.depth_max) / 2; 1003 1004 auto calculate_vertex_window_coordinates = [&](GPU::Vertex& vertex) { 1005 auto const one_over_w = 1 / vertex.clip_coordinates.w(); 1006 auto const ndc_coordinates = vertex.clip_coordinates.xyz() * one_over_w; 1007 1008 vertex.window_coordinates = { 1009 viewport_center_x + ndc_coordinates.x() * viewport_half_width, 1010 viewport_center_y + ndc_coordinates.y() * viewport_half_height, 1011 depth_halfway + ndc_coordinates.z() * depth_half_range, 1012 one_over_w, 1013 }; 1014 }; 1015 1016 // Process points 1017 if (primitive_type == GPU::PrimitiveType::Points) { 1018 m_clipper.clip_points_against_frustum(vertices); 1019 for (auto& vertex : vertices) { 1020 calculate_vertex_window_coordinates(vertex); 1021 rasterize_point(vertex); 1022 } 1023 return; 1024 } 1025 1026 // Process lines, line loop and line strips 1027 auto rasterize_line_segment = [&](GPU::Vertex& from, GPU::Vertex& to) { 1028 if (!m_clipper.clip_line_against_frustum(from, to)) 1029 return; 1030 1031 calculate_vertex_window_coordinates(from); 1032 calculate_vertex_window_coordinates(to); 1033 1034 rasterize_line(from, to); 1035 }; 1036 if (primitive_type == GPU::PrimitiveType::Lines) { 1037 if (vertices.size() < 2) 1038 return; 1039 for (size_t i = 0; i < vertices.size() - 1; i += 2) 1040 rasterize_line_segment(vertices[i], vertices[i + 1]); 1041 return; 1042 } else if (primitive_type == GPU::PrimitiveType::LineLoop) { 1043 if (vertices.size() < 2) 1044 return; 1045 for (size_t i = 0; i < vertices.size(); ++i) 1046 rasterize_line_segment(vertices[i], vertices[(i + 1) % vertices.size()]); 1047 return; 1048 } else if (primitive_type == GPU::PrimitiveType::LineStrip) { 1049 if (vertices.size() < 2) 1050 return; 1051 for (size_t i = 0; i < vertices.size() - 1; ++i) 1052 rasterize_line_segment(vertices[i], vertices[i + 1]); 1053 return; 1054 } 1055 1056 // Let's construct some triangles 1057 m_triangle_list.clear_with_capacity(); 1058 m_processed_triangles.clear_with_capacity(); 1059 if (primitive_type == GPU::PrimitiveType::Triangles) { 1060 Triangle triangle; 1061 if (vertices.size() < 3) 1062 return; 1063 for (size_t i = 0; i < vertices.size() - 2; i += 3) { 1064 triangle.vertices[0] = vertices.at(i); 1065 triangle.vertices[1] = vertices.at(i + 1); 1066 triangle.vertices[2] = vertices.at(i + 2); 1067 1068 m_triangle_list.append(triangle); 1069 } 1070 } else if (primitive_type == GPU::PrimitiveType::Quads) { 1071 // We need to construct two triangles to form the quad 1072 Triangle triangle; 1073 if (vertices.size() < 4) 1074 return; 1075 for (size_t i = 0; i < vertices.size() - 3; i += 4) { 1076 // Triangle 1 1077 triangle.vertices[0] = vertices.at(i); 1078 triangle.vertices[1] = vertices.at(i + 1); 1079 triangle.vertices[2] = vertices.at(i + 2); 1080 m_triangle_list.append(triangle); 1081 1082 // Triangle 2 1083 triangle.vertices[0] = vertices.at(i + 2); 1084 triangle.vertices[1] = vertices.at(i + 3); 1085 triangle.vertices[2] = vertices.at(i); 1086 m_triangle_list.append(triangle); 1087 } 1088 } else if (primitive_type == GPU::PrimitiveType::TriangleFan) { 1089 Triangle triangle; 1090 triangle.vertices[0] = vertices.at(0); // Root vertex is always the vertex defined first 1091 1092 // This is technically `n-2` triangles. We start at index 1 1093 for (size_t i = 1; i < vertices.size() - 1; i++) { 1094 triangle.vertices[1] = vertices.at(i); 1095 triangle.vertices[2] = vertices.at(i + 1); 1096 m_triangle_list.append(triangle); 1097 } 1098 } else if (primitive_type == GPU::PrimitiveType::TriangleStrip) { 1099 Triangle triangle; 1100 if (vertices.size() < 3) 1101 return; 1102 for (size_t i = 0; i < vertices.size() - 2; i++) { 1103 if (i % 2 == 0) { 1104 triangle.vertices[0] = vertices.at(i); 1105 triangle.vertices[1] = vertices.at(i + 1); 1106 triangle.vertices[2] = vertices.at(i + 2); 1107 } else { 1108 triangle.vertices[0] = vertices.at(i + 1); 1109 triangle.vertices[1] = vertices.at(i); 1110 triangle.vertices[2] = vertices.at(i + 2); 1111 } 1112 m_triangle_list.append(triangle); 1113 } 1114 } 1115 1116 // Clip triangles 1117 for (auto& triangle : m_triangle_list) { 1118 m_clipped_vertices.clear_with_capacity(); 1119 m_clipped_vertices.append(triangle.vertices[0]); 1120 m_clipped_vertices.append(triangle.vertices[1]); 1121 m_clipped_vertices.append(triangle.vertices[2]); 1122 m_clipper.clip_triangle_against_frustum(m_clipped_vertices); 1123 1124 if (m_clip_planes.size() > 0) 1125 m_clipper.clip_triangle_against_user_defined(m_clipped_vertices, m_clip_planes); 1126 1127 if (m_clipped_vertices.size() < 3) 1128 continue; 1129 1130 for (auto& vertex : m_clipped_vertices) 1131 calculate_vertex_window_coordinates(vertex); 1132 1133 Triangle tri; 1134 tri.vertices[0] = m_clipped_vertices[0]; 1135 for (size_t i = 1; i < m_clipped_vertices.size() - 1; i++) { 1136 tri.vertices[1] = m_clipped_vertices[i]; 1137 tri.vertices[2] = m_clipped_vertices[i + 1]; 1138 m_processed_triangles.append(tri); 1139 } 1140 } 1141 1142 for (auto& triangle : m_processed_triangles) 1143 rasterize_triangle(triangle); 1144} 1145 1146ALWAYS_INLINE void Device::shade_fragments(PixelQuad& quad) 1147{ 1148 if (m_current_fragment_shader) { 1149 m_shader_processor.execute(quad, *m_current_fragment_shader); 1150 return; 1151 } 1152 1153 Array<Vector4<f32x4>, GPU::NUM_TEXTURE_UNITS> texture_stage_texel; 1154 1155 auto current_color = quad.get_input_vector4(SHADER_INPUT_VERTEX_COLOR); 1156 1157 for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) { 1158 if (!m_texture_unit_configuration[i].enabled) 1159 continue; 1160 auto const& sampler = m_samplers[i]; 1161 1162 // OpenGL 2.0 ¶ 3.5.1 states (in a roundabout way) that texture coordinates must be divided by Q 1163 auto homogeneous_texture_coordinate = quad.get_input_vector4(SHADER_INPUT_FIRST_TEXCOORD + i * 4); 1164 auto texel = sampler.sample_2d(homogeneous_texture_coordinate.xy() / homogeneous_texture_coordinate.w()); 1165 INCREASE_STATISTICS_COUNTER(g_num_sampler_calls, 1); 1166 if (m_samplers_need_texture_staging) 1167 texture_stage_texel[i] = texel; 1168 1169 // FIXME: implement support for GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_INTENSITY and GL_RGB internal formats 1170 auto& fixed_function_env = sampler.config().fixed_function_texture_environment; 1171 switch (fixed_function_env.env_mode) { 1172 case GPU::TextureEnvMode::Add: 1173 current_color.set_x(current_color.x() + texel.x()); 1174 current_color.set_y(current_color.y() + texel.y()); 1175 current_color.set_z(current_color.z() + texel.z()); 1176 current_color.set_w(current_color.w() * texel.w()); 1177 break; 1178 case GPU::TextureEnvMode::Blend: { 1179 auto blend_color = expand4(fixed_function_env.color); 1180 current_color.set_x(mix(current_color.x(), blend_color.x(), texel.x())); 1181 current_color.set_y(mix(current_color.y(), blend_color.y(), texel.y())); 1182 current_color.set_z(mix(current_color.z(), blend_color.z(), texel.z())); 1183 current_color.set_w(current_color.w() * texel.w()); 1184 break; 1185 } 1186 case GPU::TextureEnvMode::Combine: { 1187 auto get_source_color = [&](GPU::TextureSource source, u8 texture_stage) { 1188 switch (source) { 1189 case GPU::TextureSource::Constant: 1190 return expand4(fixed_function_env.color); 1191 case GPU::TextureSource::Previous: 1192 return current_color; 1193 case GPU::TextureSource::PrimaryColor: 1194 return quad.get_input_vector4(SHADER_INPUT_VERTEX_COLOR); 1195 case GPU::TextureSource::Texture: 1196 return texel; 1197 case GPU::TextureSource::TextureStage: 1198 return texture_stage_texel[texture_stage]; 1199 } 1200 VERIFY_NOT_REACHED(); 1201 }; 1202 auto get_argument_value = [](GPU::TextureOperand operand, auto value) { 1203 switch (operand) { 1204 case GPU::TextureOperand::OneMinusSourceAlpha: 1205 case GPU::TextureOperand::OneMinusSourceColor: 1206 return expand4(FloatVector4 { 1.f, 1.f, 1.f, 1.f }) - value; 1207 case GPU::TextureOperand::SourceAlpha: 1208 case GPU::TextureOperand::SourceColor: 1209 return value; 1210 } 1211 VERIFY_NOT_REACHED(); 1212 }; 1213 auto calculate_combinator = [](GPU::TextureCombinator combinator, auto arg0, auto arg1, auto arg2) { 1214 switch (combinator) { 1215 case GPU::TextureCombinator::Add: 1216 return arg0 + arg1; 1217 case GPU::TextureCombinator::AddSigned: 1218 return arg0 + arg1 - expand4(FloatVector4 { .5f, .5f, .5f, .5f }); 1219 case GPU::TextureCombinator::Dot3RGB: 1220 case GPU::TextureCombinator::Dot3RGBA: { 1221 auto scalar = 4.f * ((arg0.x() - .5f) * (arg1.x() - .5f) + (arg0.y() - 0.5f) * (arg1.y() - 0.5f) + (arg0.z() - 0.5f) * (arg1.z() - 0.5f)); 1222 return Vector4<f32x4> { scalar, scalar, scalar, scalar }; 1223 } 1224 case GPU::TextureCombinator::Interpolate: 1225 return mix(arg0, arg1, arg2); 1226 case GPU::TextureCombinator::Modulate: 1227 return arg0 * arg1; 1228 case GPU::TextureCombinator::Replace: 1229 return arg0; 1230 case GPU::TextureCombinator::Subtract: 1231 return arg0 - arg1; 1232 } 1233 VERIFY_NOT_REACHED(); 1234 }; 1235 auto calculate_color = [&](GPU::TextureCombinator combinator, auto& operands, auto& sources, u8 texture_stage) { 1236 auto arg0 = get_argument_value(operands[0], get_source_color(sources[0], texture_stage)); 1237 auto arg1 = get_argument_value(operands[1], get_source_color(sources[1], texture_stage)); 1238 auto arg2 = get_argument_value(operands[2], get_source_color(sources[2], texture_stage)); 1239 return calculate_combinator(combinator, arg0, arg1, arg2); 1240 }; 1241 1242 auto rgb_color = calculate_color( 1243 fixed_function_env.rgb_combinator, 1244 fixed_function_env.rgb_operand, 1245 fixed_function_env.rgb_source, 1246 fixed_function_env.rgb_source_texture_stage); 1247 auto alpha_color = calculate_color( 1248 fixed_function_env.alpha_combinator, 1249 fixed_function_env.alpha_operand, 1250 fixed_function_env.alpha_source, 1251 fixed_function_env.alpha_source_texture_stage); 1252 1253 current_color.set_x(rgb_color.x() * fixed_function_env.rgb_scale); 1254 current_color.set_y(rgb_color.y() * fixed_function_env.rgb_scale); 1255 current_color.set_z(rgb_color.z() * fixed_function_env.rgb_scale); 1256 current_color.set_w(alpha_color.w() * fixed_function_env.alpha_scale); 1257 1258 current_color.clamp(expand4(0.f), expand4(1.f)); 1259 break; 1260 } 1261 case GPU::TextureEnvMode::Decal: { 1262 auto dst_alpha = texel.w(); 1263 current_color.set_x(mix(current_color.x(), texel.x(), dst_alpha)); 1264 current_color.set_y(mix(current_color.y(), texel.y(), dst_alpha)); 1265 current_color.set_z(mix(current_color.z(), texel.z(), dst_alpha)); 1266 break; 1267 } 1268 case GPU::TextureEnvMode::Modulate: 1269 current_color = current_color * texel; 1270 break; 1271 case GPU::TextureEnvMode::Replace: 1272 current_color = texel; 1273 break; 1274 } 1275 } 1276 1277 // Calculate fog 1278 // Math from here: https://opengl-notes.readthedocs.io/en/latest/topics/texturing/aliasing.html 1279 1280 if (m_options.fog_enabled) { 1281 f32x4 factor; 1282 switch (m_options.fog_mode) { 1283 case GPU::FogMode::Linear: 1284 factor = (m_options.fog_end - quad.fog_depth) * m_one_over_fog_depth; 1285 break; 1286 case GPU::FogMode::Exp: { 1287 auto argument = -m_options.fog_density * quad.fog_depth; 1288 factor = exp_approximate(argument); 1289 } break; 1290 case GPU::FogMode::Exp2: { 1291 auto argument = m_options.fog_density * quad.fog_depth; 1292 argument *= -argument; 1293 factor = exp_approximate(argument); 1294 } break; 1295 default: 1296 VERIFY_NOT_REACHED(); 1297 } 1298 1299 // Mix texel's RGB with fog's RBG - leave alpha alone 1300 auto fog_color = expand4(m_options.fog_color); 1301 current_color.set_x(mix(fog_color.x(), current_color.x(), factor)); 1302 current_color.set_y(mix(fog_color.y(), current_color.y(), factor)); 1303 current_color.set_z(mix(fog_color.z(), current_color.z(), factor)); 1304 } 1305 1306 quad.set_output(SHADER_OUTPUT_FIRST_COLOR, current_color.x()); 1307 quad.set_output(SHADER_OUTPUT_FIRST_COLOR + 1, current_color.y()); 1308 quad.set_output(SHADER_OUTPUT_FIRST_COLOR + 2, current_color.z()); 1309 // Multiply coverage with the fragment's alpha to obtain the final alpha value 1310 quad.set_output(SHADER_OUTPUT_FIRST_COLOR + 3, current_color.w() * quad.coverage); 1311} 1312 1313void Device::resize(Gfx::IntSize size) 1314{ 1315 auto frame_buffer_or_error = FrameBuffer<GPU::ColorType, GPU::DepthType, GPU::StencilType>::try_create(size); 1316 m_frame_buffer = MUST(frame_buffer_or_error); 1317} 1318 1319void Device::clear_color(FloatVector4 const& color) 1320{ 1321 auto const fill_color = to_argb32(color); 1322 1323 auto clear_rect = m_frame_buffer->rect(); 1324 if (m_options.scissor_enabled) 1325 clear_rect.intersect(m_options.scissor_box); 1326 1327 m_frame_buffer->color_buffer()->fill(fill_color, clear_rect); 1328} 1329 1330void Device::clear_depth(GPU::DepthType depth) 1331{ 1332 auto clear_rect = m_frame_buffer->rect(); 1333 if (m_options.scissor_enabled) 1334 clear_rect.intersect(m_options.scissor_box); 1335 1336 m_frame_buffer->depth_buffer()->fill(depth, clear_rect); 1337} 1338 1339void Device::clear_stencil(GPU::StencilType value) 1340{ 1341 auto clear_rect = m_frame_buffer->rect(); 1342 if (m_options.scissor_enabled) 1343 clear_rect.intersect(m_options.scissor_box); 1344 1345 m_frame_buffer->stencil_buffer()->fill(value, clear_rect); 1346} 1347 1348GPU::ImageDataLayout Device::color_buffer_data_layout(Vector2<u32> size, Vector2<i32> offset) 1349{ 1350 return { 1351 .pixel_type = { 1352 .format = GPU::PixelFormat::BGRA, 1353 .bits = GPU::PixelComponentBits::B8_8_8_8, 1354 .data_type = GPU::PixelDataType::UnsignedInt, 1355 .components_order = GPU::ComponentsOrder::Reversed, 1356 }, 1357 .dimensions = { 1358 .width = static_cast<u32>(m_frame_buffer->rect().width()), 1359 .height = static_cast<u32>(m_frame_buffer->rect().height()), 1360 .depth = 1, 1361 }, 1362 .selection = { 1363 .offset_x = offset.x(), 1364 .offset_y = offset.y(), 1365 .offset_z = 0, 1366 .width = size.x(), 1367 .height = size.y(), 1368 .depth = 1, 1369 }, 1370 }; 1371} 1372 1373GPU::ImageDataLayout Device::depth_buffer_data_layout(Vector2<u32> size, Vector2<i32> offset) 1374{ 1375 return { 1376 .pixel_type = { 1377 .format = GPU::PixelFormat::DepthComponent, 1378 .bits = GPU::PixelComponentBits::AllBits, 1379 .data_type = GPU::PixelDataType::Float, 1380 }, 1381 .dimensions = { 1382 .width = static_cast<u32>(m_frame_buffer->rect().width()), 1383 .height = static_cast<u32>(m_frame_buffer->rect().height()), 1384 .depth = 1, 1385 }, 1386 .selection = { 1387 .offset_x = offset.x(), 1388 .offset_y = offset.y(), 1389 .offset_z = 0, 1390 .width = size.x(), 1391 .height = size.y(), 1392 .depth = 1, 1393 }, 1394 }; 1395} 1396 1397void Device::blit_from_color_buffer(Gfx::Bitmap& target) 1398{ 1399 m_frame_buffer->color_buffer()->blit_flipped_to_bitmap(target, m_frame_buffer->rect()); 1400 1401 if constexpr (ENABLE_STATISTICS_OVERLAY) 1402 draw_statistics_overlay(target); 1403} 1404 1405void Device::blit_from_color_buffer(NonnullRefPtr<GPU::Image> image, u32 level, Vector2<u32> input_size, Vector2<i32> input_offset, Vector3<i32> output_offset) 1406{ 1407 auto input_layout = color_buffer_data_layout(input_size, input_offset); 1408 auto const* input_data = m_frame_buffer->color_buffer()->scanline(0); 1409 1410 auto const& softgpu_image = reinterpret_cast<Image*>(image.ptr()); 1411 auto output_layout = softgpu_image->image_data_layout(level, output_offset); 1412 auto* output_data = softgpu_image->texel_pointer(level, 0, 0, 0); 1413 1414 PixelConverter converter { input_layout, output_layout }; 1415 auto conversion_result = converter.convert(input_data, output_data, {}); 1416 if (conversion_result.is_error()) 1417 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1418} 1419 1420void Device::blit_from_color_buffer(void* output_data, Vector2<i32> input_offset, GPU::ImageDataLayout const& output_layout) 1421{ 1422 auto const& output_selection = output_layout.selection; 1423 auto input_layout = color_buffer_data_layout({ output_selection.width, output_selection.height }, input_offset); 1424 1425 PixelConverter converter { input_layout, output_layout }; 1426 auto const* input_data = m_frame_buffer->color_buffer()->scanline(0); 1427 auto conversion_result = converter.convert(input_data, output_data, {}); 1428 if (conversion_result.is_error()) 1429 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1430} 1431 1432void Device::blit_from_depth_buffer(void* output_data, Vector2<i32> input_offset, GPU::ImageDataLayout const& output_layout) 1433{ 1434 auto const& output_selection = output_layout.selection; 1435 auto input_layout = depth_buffer_data_layout({ output_selection.width, output_selection.height }, input_offset); 1436 1437 PixelConverter converter { input_layout, output_layout }; 1438 auto const* input_data = m_frame_buffer->depth_buffer()->scanline(0); 1439 auto conversion_result = converter.convert(input_data, output_data, {}); 1440 if (conversion_result.is_error()) 1441 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1442} 1443 1444void Device::blit_from_depth_buffer(NonnullRefPtr<GPU::Image> image, u32 level, Vector2<u32> input_size, Vector2<i32> input_offset, Vector3<i32> output_offset) 1445{ 1446 auto input_layout = depth_buffer_data_layout(input_size, input_offset); 1447 auto const* input_data = m_frame_buffer->depth_buffer()->scanline(0); 1448 1449 auto const& softgpu_image = reinterpret_cast<Image*>(image.ptr()); 1450 auto output_layout = softgpu_image->image_data_layout(level, output_offset); 1451 auto* output_data = softgpu_image->texel_pointer(level, 0, 0, 0); 1452 1453 PixelConverter converter { input_layout, output_layout }; 1454 auto conversion_result = converter.convert(input_data, output_data, {}); 1455 if (conversion_result.is_error()) 1456 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1457} 1458 1459void Device::blit_to_color_buffer_at_raster_position(void const* input_data, GPU::ImageDataLayout const& input_layout) 1460{ 1461 if (!m_raster_position.valid) 1462 return; 1463 1464 auto input_selection = input_layout.selection; 1465 INCREASE_STATISTICS_COUNTER(g_num_pixels, input_selection.width * input_selection.height); 1466 INCREASE_STATISTICS_COUNTER(g_num_pixels_shaded, input_selection.width * input_selection.height); 1467 1468 auto const rasterization_rect = get_rasterization_rect_of_size({ input_selection.width, input_selection.height }); 1469 auto output_layout = color_buffer_data_layout( 1470 { static_cast<u32>(rasterization_rect.width()), static_cast<u32>(rasterization_rect.height()) }, 1471 { rasterization_rect.x(), rasterization_rect.y() }); 1472 1473 PixelConverter converter { input_layout, output_layout }; 1474 auto* output_data = m_frame_buffer->color_buffer()->scanline(0); 1475 auto conversion_result = converter.convert(input_data, output_data, {}); 1476 if (conversion_result.is_error()) 1477 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1478} 1479 1480void Device::blit_to_depth_buffer_at_raster_position(void const* input_data, GPU::ImageDataLayout const& input_layout) 1481{ 1482 if (!m_raster_position.valid) 1483 return; 1484 1485 auto input_selection = input_layout.selection; 1486 auto const rasterization_rect = get_rasterization_rect_of_size({ input_selection.width, input_selection.height }); 1487 auto output_layout = depth_buffer_data_layout( 1488 { static_cast<u32>(rasterization_rect.width()), static_cast<u32>(rasterization_rect.height()) }, 1489 { rasterization_rect.x(), rasterization_rect.y() }); 1490 1491 PixelConverter converter { input_layout, output_layout }; 1492 auto* output_data = m_frame_buffer->depth_buffer()->scanline(0); 1493 auto conversion_result = converter.convert(input_data, output_data, {}); 1494 if (conversion_result.is_error()) 1495 dbgln("Pixel conversion failed: {}", conversion_result.error().string_literal()); 1496} 1497 1498void Device::draw_statistics_overlay(Gfx::Bitmap& target) 1499{ 1500 static Core::ElapsedTimer timer; 1501 static String debug_string; 1502 static int frame_counter; 1503 1504 frame_counter++; 1505 i64 milliseconds = 0; 1506 if (timer.is_valid()) 1507 milliseconds = timer.elapsed(); 1508 else 1509 timer.start(); 1510 1511 Gfx::Painter painter { target }; 1512 1513 if (milliseconds > MILLISECONDS_PER_STATISTICS_PERIOD) { 1514 1515 int num_rendertarget_pixels = m_frame_buffer->rect().size().area(); 1516 1517 StringBuilder builder; 1518 builder.appendff("Timings : {:.1}ms {:.1}FPS\n", 1519 static_cast<double>(milliseconds) / frame_counter, 1520 (milliseconds > 0) ? 1000.0 * frame_counter / milliseconds : 9999.0); 1521 builder.appendff("Triangles : {}\n", g_num_rasterized_triangles); 1522 builder.appendff("SIMD usage : {}%\n", g_num_quads > 0 ? g_num_pixels_shaded * 25 / g_num_quads : 0); 1523 builder.appendff("Pixels : {}, Stencil: {}%, Shaded: {}%, Blended: {}%, Overdraw: {}%\n", 1524 g_num_pixels, 1525 g_num_pixels > 0 ? g_num_stencil_writes * 100 / g_num_pixels : 0, 1526 g_num_pixels > 0 ? g_num_pixels_shaded * 100 / g_num_pixels : 0, 1527 g_num_pixels_shaded > 0 ? g_num_pixels_blended * 100 / g_num_pixels_shaded : 0, 1528 num_rendertarget_pixels > 0 ? g_num_pixels_shaded * 100 / num_rendertarget_pixels - 100 : 0); 1529 builder.appendff("Sampler calls: {}\n", g_num_sampler_calls); 1530 1531 debug_string = builder.to_string().release_value_but_fixme_should_propagate_errors(); 1532 1533 frame_counter = 0; 1534 timer.start(); 1535 } 1536 1537 g_num_rasterized_triangles = 0; 1538 g_num_pixels = 0; 1539 g_num_pixels_shaded = 0; 1540 g_num_pixels_blended = 0; 1541 g_num_sampler_calls = 0; 1542 g_num_stencil_writes = 0; 1543 g_num_quads = 0; 1544 1545 auto& font = Gfx::FontDatabase::default_fixed_width_font(); 1546 1547 for (int y = -1; y < 2; y++) 1548 for (int x = -1; x < 2; x++) 1549 if (x != 0 && y != 0) 1550 painter.draw_text(target.rect().translated(x + 2, y + 2), debug_string, font, Gfx::TextAlignment::TopLeft, Gfx::Color::Black); 1551 1552 painter.draw_text(target.rect().translated(2, 2), debug_string, font, Gfx::TextAlignment::TopLeft, Gfx::Color::White); 1553} 1554 1555void Device::set_options(GPU::RasterizerOptions const& options) 1556{ 1557 m_options = options; 1558 if (m_options.fog_enabled) 1559 m_one_over_fog_depth = 1.f / (m_options.fog_end - m_options.fog_start); 1560} 1561 1562void Device::set_light_model_params(GPU::LightModelParameters const& lighting_model) 1563{ 1564 m_lighting_model = lighting_model; 1565} 1566 1567NonnullRefPtr<GPU::Image> Device::create_image(GPU::PixelFormat const& pixel_format, u32 width, u32 height, u32 depth, u32 max_levels) 1568{ 1569 VERIFY(width > 0); 1570 VERIFY(height > 0); 1571 VERIFY(depth > 0); 1572 VERIFY(max_levels > 0); 1573 1574 return adopt_ref(*new Image(this, pixel_format, width, height, depth, max_levels)); 1575} 1576 1577ErrorOr<NonnullRefPtr<GPU::Shader>> Device::create_shader(GPU::IR::Shader const& intermediate_representation) 1578{ 1579 ShaderCompiler compiler; 1580 auto shader = TRY(compiler.compile(this, intermediate_representation)); 1581 return shader; 1582} 1583 1584void Device::set_sampler_config(unsigned sampler, GPU::SamplerConfig const& config) 1585{ 1586 VERIFY(config.bound_image.is_null() || config.bound_image->ownership_token() == this); 1587 1588 m_samplers[sampler].set_config(config); 1589 1590 m_samplers_need_texture_staging = any_of(m_samplers, [](auto const& sampler) { 1591 auto const& fixed_function_env = sampler.config().fixed_function_texture_environment; 1592 if (fixed_function_env.env_mode != GPU::TextureEnvMode::Combine) 1593 return false; 1594 return any_of(fixed_function_env.alpha_source, [](auto texture_source) { return texture_source == GPU::TextureSource::TextureStage; }) 1595 || any_of(fixed_function_env.rgb_source, [](auto texture_source) { return texture_source == GPU::TextureSource::TextureStage; }); 1596 }); 1597} 1598 1599void Device::set_light_state(unsigned int light_id, GPU::Light const& light) 1600{ 1601 m_lights.at(light_id) = light; 1602} 1603 1604void Device::set_material_state(GPU::Face face, GPU::Material const& material) 1605{ 1606 m_materials[face] = material; 1607} 1608 1609void Device::set_stencil_configuration(GPU::Face face, GPU::StencilConfiguration const& stencil_configuration) 1610{ 1611 m_stencil_configuration[face] = stencil_configuration; 1612} 1613 1614void Device::set_texture_unit_configuration(GPU::TextureUnitIndex index, GPU::TextureUnitConfiguration const& configuration) 1615{ 1616 m_texture_unit_configuration[index] = configuration; 1617} 1618 1619void Device::set_raster_position(GPU::RasterPosition const& raster_position) 1620{ 1621 m_raster_position = raster_position; 1622} 1623 1624void Device::set_clip_planes(Vector<FloatVector4> const& clip_planes) 1625{ 1626 m_clip_planes = clip_planes; 1627} 1628 1629void Device::set_raster_position(FloatVector4 const& position, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform) 1630{ 1631 auto const eye_coordinates = model_view_transform * position; 1632 auto const clip_coordinates = projection_transform * eye_coordinates; 1633 1634 // FIXME: implement clipping 1635 m_raster_position.valid = true; 1636 1637 auto ndc_coordinates = clip_coordinates / clip_coordinates.w(); 1638 ndc_coordinates.set_w(clip_coordinates.w()); 1639 1640 auto const viewport = m_options.viewport; 1641 auto const viewport_half_width = viewport.width() / 2.0f; 1642 auto const viewport_half_height = viewport.height() / 2.0f; 1643 auto const viewport_center_x = viewport.x() + viewport_half_width; 1644 auto const viewport_center_y = viewport.y() + viewport_half_height; 1645 auto const depth_half_range = (m_options.depth_max - m_options.depth_min) / 2; 1646 auto const depth_halfway = (m_options.depth_min + m_options.depth_max) / 2; 1647 1648 // FIXME: implement other raster position properties such as color and texcoords 1649 1650 m_raster_position.window_coordinates = { 1651 viewport_center_x + ndc_coordinates.x() * viewport_half_width, 1652 viewport_center_y + ndc_coordinates.y() * viewport_half_height, 1653 depth_halfway + ndc_coordinates.z() * depth_half_range, 1654 ndc_coordinates.w(), 1655 }; 1656 1657 m_raster_position.eye_coordinate_distance = eye_coordinates.length(); 1658} 1659 1660void Device::bind_fragment_shader(RefPtr<GPU::Shader> shader) 1661{ 1662 VERIFY(shader.is_null() || shader->ownership_token() == this); 1663 1664 if (shader.is_null()) { 1665 m_current_fragment_shader = nullptr; 1666 return; 1667 } 1668 1669 auto softgpu_shader = static_ptr_cast<Shader>(shader); 1670 m_current_fragment_shader = softgpu_shader; 1671} 1672 1673Gfx::IntRect Device::get_rasterization_rect_of_size(Gfx::IntSize size) const 1674{ 1675 // Round the X and Y floating point coordinates to the nearest integer; OpenGL 1.5 spec: 1676 // "Any fragments whose centers lie inside of this rectangle (or on its bottom or left 1677 // boundaries) are produced in correspondence with this particular group of elements." 1678 return { 1679 round_to<int>(m_raster_position.window_coordinates.x()), 1680 round_to<int>(m_raster_position.window_coordinates.y()), 1681 size.width(), 1682 size.height(), 1683 }; 1684} 1685 1686} 1687 1688extern "C" { 1689 1690GPU::Device* serenity_gpu_create_device(Gfx::IntSize size) 1691{ 1692 return make<SoftGPU::Device>(size).leak_ptr(); 1693} 1694}