Texture.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
  3. * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
  4. * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Debug.h>
  9. #include <LibGL/GLContext.h>
  10. #include <LibGL/Image.h>
  11. #include <LibGPU/ImageDataLayout.h>
  12. namespace GL {
  13. void GLContext::gl_active_texture(GLenum texture)
  14. {
  15. RETURN_WITH_ERROR_IF(texture < GL_TEXTURE0 || texture >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  16. m_active_texture_unit_index = texture - GL_TEXTURE0;
  17. m_active_texture_unit = &m_texture_units.at(m_active_texture_unit_index);
  18. }
  19. void GLContext::gl_bind_texture(GLenum target, GLuint texture)
  20. {
  21. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  22. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_1D
  23. && target != GL_TEXTURE_2D
  24. && target != GL_TEXTURE_3D
  25. && target != GL_TEXTURE_1D_ARRAY
  26. && target != GL_TEXTURE_2D_ARRAY
  27. && target != GL_TEXTURE_CUBE_MAP,
  28. GL_INVALID_ENUM);
  29. // FIXME: We only support GL_TEXTURE_2D for now
  30. if (target != GL_TEXTURE_2D) {
  31. dbgln("gl_bind_texture(target = {:#x}): currently only GL_TEXTURE_2D is supported", target);
  32. return;
  33. }
  34. RefPtr<Texture2D> texture_2d;
  35. if (texture == 0) {
  36. // Texture name 0 refers to the default texture
  37. texture_2d = get_default_texture<Texture2D>(target);
  38. } else {
  39. // Find this texture name in our previously allocated textures
  40. auto it = m_allocated_textures.find(texture);
  41. if (it != m_allocated_textures.end()) {
  42. auto texture_object = it->value;
  43. if (!texture_object.is_null()) {
  44. // Texture must have been created with the same target
  45. RETURN_WITH_ERROR_IF(!texture_object->is_texture_2d(), GL_INVALID_OPERATION);
  46. texture_2d = static_cast<Texture2D*>(texture_object.ptr());
  47. }
  48. }
  49. // OpenGL 1.x supports binding texture names that were not previously generated by glGenTextures.
  50. // If there is not an allocated texture, meaning it was not previously generated by glGenTextures,
  51. // we can keep texture_object null to both allocate and bind the texture with the passed in texture name.
  52. // FIXME: Later OpenGL versions such as 4.x enforce that texture names being bound were previously generated
  53. // by glGenTextures.
  54. if (!texture_2d) {
  55. texture_2d = adopt_ref(*new Texture2D());
  56. m_allocated_textures.set(texture, texture_2d);
  57. }
  58. }
  59. m_active_texture_unit->set_texture_2d_target_texture(texture_2d);
  60. m_sampler_config_is_dirty = true;
  61. }
  62. void GLContext::gl_client_active_texture(GLenum target)
  63. {
  64. RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  65. m_client_active_texture = target - GL_TEXTURE0;
  66. }
  67. void GLContext::gl_copy_tex_image_2d(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
  68. {
  69. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_image_2d, target, level, internalformat, x, y, width, height, border);
  70. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  71. // FIXME: implement
  72. dbgln_if(GL_DEBUG, "GLContext FIXME: implement gl_copy_tex_image_2d({:#x}, {}, {:#x}, {}, {}, {}, {}, {})",
  73. target, level, internalformat, x, y, width, height, border);
  74. }
  75. void GLContext::gl_copy_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
  76. {
  77. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_copy_tex_sub_image_2d, target, level, xoffset, yoffset, x, y, width, height);
  78. RETURN_WITH_ERROR_IF(!(target == GL_TEXTURE_2D || target == GL_TEXTURE_1D_ARRAY), GL_INVALID_ENUM);
  79. RETURN_WITH_ERROR_IF(level < 0, GL_INVALID_VALUE);
  80. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  81. // FIXME: implement
  82. dbgln_if(GL_DEBUG, "GLContext FIXME: implement gl_copy_tex_sub_image_2d({:#x}, {}, {}, {}, {}, {}, {}, {})",
  83. target, level, xoffset, yoffset, x, y, width, height);
  84. }
  85. void GLContext::gl_delete_textures(GLsizei n, GLuint const* textures)
  86. {
  87. RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
  88. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  89. for (auto i = 0; i < n; i++) {
  90. GLuint name = textures[i];
  91. if (name == 0)
  92. continue;
  93. auto texture_object = m_allocated_textures.find(name);
  94. if (texture_object == m_allocated_textures.end() || texture_object->value.is_null())
  95. continue;
  96. m_name_allocator.free(name);
  97. auto texture = texture_object->value;
  98. // Check all texture units
  99. for (auto& texture_unit : m_texture_units) {
  100. if (texture->is_texture_2d() && texture_unit.texture_2d_target_texture() == texture) {
  101. // If a texture that is currently bound is deleted, the binding reverts to 0 (the default texture)
  102. texture_unit.set_texture_2d_target_texture(get_default_texture<Texture2D>(GL_TEXTURE_2D));
  103. }
  104. }
  105. m_allocated_textures.remove(name);
  106. }
  107. }
  108. void GLContext::gl_gen_textures(GLsizei n, GLuint* textures)
  109. {
  110. RETURN_WITH_ERROR_IF(n < 0, GL_INVALID_VALUE);
  111. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  112. m_name_allocator.allocate(n, textures);
  113. // Initialize all texture names with a nullptr
  114. for (auto i = 0; i < n; ++i) {
  115. GLuint name = textures[i];
  116. m_allocated_textures.set(name, nullptr);
  117. }
  118. }
  119. void GLContext::gl_get_tex_image(GLenum target, GLint level, GLenum format, GLenum type, void* pixels)
  120. {
  121. RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
  122. auto pixel_type_or_error = get_validated_pixel_type(target, GL_NONE, format, type);
  123. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  124. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  125. VERIFY(!texture_2d.is_null());
  126. u32 width = texture_2d->width_at_lod(level);
  127. u32 height = texture_2d->height_at_lod(level);
  128. GPU::ImageDataLayout output_layout = {
  129. .pixel_type = pixel_type_or_error.release_value(),
  130. .packing = get_packing_specification(PackingType::Pack),
  131. .dimensions = {
  132. .width = width,
  133. .height = height,
  134. .depth = 1,
  135. },
  136. .selection = {
  137. .width = width,
  138. .height = height,
  139. .depth = 1,
  140. },
  141. };
  142. texture_2d->download_texture_data(level, output_layout, pixels);
  143. }
  144. void GLContext::gl_get_tex_parameter_integerv(GLenum target, GLint level, GLenum pname, GLint* params)
  145. {
  146. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  147. // FIXME: support targets other than GL_TEXTURE_2D
  148. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  149. // FIXME: support other parameter names
  150. RETURN_WITH_ERROR_IF(pname < GL_TEXTURE_WIDTH || pname > GL_TEXTURE_HEIGHT, GL_INVALID_ENUM);
  151. RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
  152. // FIXME: GL_INVALID_VALUE is generated if target is GL_TEXTURE_BUFFER and level is not zero
  153. // FIXME: GL_INVALID_OPERATION is generated if GL_TEXTURE_COMPRESSED_IMAGE_SIZE is queried on texture images with an uncompressed internal format or on proxy targets
  154. VERIFY(!m_active_texture_unit->texture_2d_target_texture().is_null());
  155. auto const texture_2d = m_active_texture_unit->texture_2d_target_texture();
  156. switch (pname) {
  157. case GL_TEXTURE_HEIGHT:
  158. *params = texture_2d->height_at_lod(level);
  159. break;
  160. case GL_TEXTURE_WIDTH:
  161. *params = texture_2d->width_at_lod(level);
  162. break;
  163. }
  164. }
  165. GLboolean GLContext::gl_is_texture(GLuint texture)
  166. {
  167. RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, GL_FALSE);
  168. if (texture == 0)
  169. return GL_FALSE;
  170. auto it = m_allocated_textures.find(texture);
  171. if (it == m_allocated_textures.end())
  172. return GL_FALSE;
  173. return it->value.is_null() ? GL_FALSE : GL_TRUE;
  174. }
  175. void GLContext::gl_multi_tex_coord(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
  176. {
  177. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_multi_tex_coord, target, s, t, r, q);
  178. RETURN_WITH_ERROR_IF(target < GL_TEXTURE0 || target >= GL_TEXTURE0 + m_device_info.num_texture_units, GL_INVALID_ENUM);
  179. m_current_vertex_tex_coord[target - GL_TEXTURE0] = { s, t, r, q };
  180. }
  181. void GLContext::gl_tex_coord(GLfloat s, GLfloat t, GLfloat r, GLfloat q)
  182. {
  183. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_coord, s, t, r, q);
  184. m_current_vertex_tex_coord[0] = { s, t, r, q };
  185. }
  186. void GLContext::gl_tex_coord_pointer(GLint size, GLenum type, GLsizei stride, void const* pointer)
  187. {
  188. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  189. RETURN_WITH_ERROR_IF(!(size == 1 || size == 2 || size == 3 || size == 4), GL_INVALID_VALUE);
  190. RETURN_WITH_ERROR_IF(!(type == GL_SHORT || type == GL_INT || type == GL_FLOAT || type == GL_DOUBLE), GL_INVALID_ENUM);
  191. RETURN_WITH_ERROR_IF(stride < 0, GL_INVALID_VALUE);
  192. auto& tex_coord_pointer = m_client_tex_coord_pointer[m_client_active_texture];
  193. tex_coord_pointer = { .size = size, .type = type, .stride = stride, .pointer = pointer };
  194. }
  195. void GLContext::gl_tex_env(GLenum target, GLenum pname, GLfloat param)
  196. {
  197. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_env, target, pname, param);
  198. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  199. // FIXME: We currently only support a subset of possible target values. Implement the rest!
  200. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_ENV, GL_INVALID_ENUM);
  201. // FIXME: We currently only support a subset of possible pname values. Implement the rest!
  202. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_ENV_MODE, GL_INVALID_ENUM);
  203. auto param_enum = static_cast<GLenum>(param);
  204. switch (param_enum) {
  205. case GL_MODULATE:
  206. case GL_REPLACE:
  207. case GL_DECAL:
  208. case GL_ADD:
  209. m_active_texture_unit->set_env_mode(param_enum);
  210. m_sampler_config_is_dirty = true;
  211. break;
  212. default:
  213. // FIXME: We currently only support a subset of possible param values. Implement the rest!
  214. dbgln_if(GL_DEBUG, "gl_tex_env({:#x}, {:#x}, {}): param unimplemented", target, pname, param);
  215. RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
  216. }
  217. }
  218. void GLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param)
  219. {
  220. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen, coord, pname, param);
  221. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  222. RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
  223. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE, GL_INVALID_ENUM);
  224. RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
  225. && param != GL_OBJECT_LINEAR
  226. && param != GL_SPHERE_MAP
  227. && param != GL_NORMAL_MAP
  228. && param != GL_REFLECTION_MAP,
  229. GL_INVALID_ENUM);
  230. RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
  231. RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
  232. GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
  233. texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
  234. m_texcoord_generation_dirty = true;
  235. }
  236. void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
  237. {
  238. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_gen_floatv, coord, pname, params);
  239. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  240. RETURN_WITH_ERROR_IF(coord < GL_S || coord > GL_Q, GL_INVALID_ENUM);
  241. RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_GEN_MODE
  242. && pname != GL_OBJECT_PLANE
  243. && pname != GL_EYE_PLANE,
  244. GL_INVALID_ENUM);
  245. GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
  246. switch (pname) {
  247. case GL_TEXTURE_GEN_MODE: {
  248. auto param = static_cast<GLenum>(params[0]);
  249. RETURN_WITH_ERROR_IF(param != GL_EYE_LINEAR
  250. && param != GL_OBJECT_LINEAR
  251. && param != GL_SPHERE_MAP
  252. && param != GL_NORMAL_MAP
  253. && param != GL_REFLECTION_MAP,
  254. GL_INVALID_ENUM);
  255. RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
  256. RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
  257. texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
  258. break;
  259. }
  260. case GL_OBJECT_PLANE:
  261. texture_coordinate_generation(m_active_texture_unit_index, capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
  262. break;
  263. case GL_EYE_PLANE: {
  264. auto const& inverse_model_view = m_model_view_matrix.inverse();
  265. auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
  266. // Note: we are allowed to store transformed coefficients here, according to the documentation on
  267. // `glGetTexGen`:
  268. //
  269. // "The returned values are those maintained in eye coordinates. They are not equal to the values
  270. // specified using glTexGen, unless the modelview matrix was identity when glTexGen was called."
  271. texture_coordinate_generation(m_active_texture_unit_index, capability).eye_plane_coefficients = inverse_model_view * input_coefficients;
  272. break;
  273. }
  274. default:
  275. VERIFY_NOT_REACHED();
  276. }
  277. m_texcoord_generation_dirty = true;
  278. }
  279. void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
  280. {
  281. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  282. auto pixel_type_or_error = get_validated_pixel_type(target, internal_format, format, type);
  283. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  284. RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
  285. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + Texture2D::MAX_TEXTURE_SIZE) || height > (2 + Texture2D::MAX_TEXTURE_SIZE), GL_INVALID_VALUE);
  286. // Check if width and height are a power of 2
  287. if (!m_device_info.supports_npot_textures) {
  288. RETURN_WITH_ERROR_IF(!is_power_of_two(width), GL_INVALID_VALUE);
  289. RETURN_WITH_ERROR_IF(!is_power_of_two(height), GL_INVALID_VALUE);
  290. }
  291. RETURN_WITH_ERROR_IF(border != 0, GL_INVALID_VALUE);
  292. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  293. VERIFY(!texture_2d.is_null());
  294. if (level == 0) {
  295. // FIXME: OpenGL has the concept of texture and mipmap completeness. A texture has to fulfill certain criteria to be considered complete.
  296. // Trying to render while an incomplete texture is bound will result in an error.
  297. // Here we simply create a complete device image when mipmap level 0 is attached to the texture object. This has the unfortunate side effect
  298. // that constructing GL textures in any but the default mipmap order, going from level 0 upwards will cause mip levels to stay uninitialized.
  299. // To be spec compliant we should create the device image once the texture has become complete and is used for rendering the first time.
  300. // All images that were attached before the device image was created need to be stored somewhere to be used to initialize the device image once complete.
  301. auto internal_pixel_format = pixel_format_for_internal_format(internal_format);
  302. texture_2d->set_device_image(m_rasterizer->create_image(internal_pixel_format, width, height, 1, 999, 1));
  303. m_sampler_config_is_dirty = true;
  304. }
  305. GPU::ImageDataLayout input_layout = {
  306. .pixel_type = pixel_type_or_error.release_value(),
  307. .packing = get_packing_specification(PackingType::Unpack),
  308. .dimensions = {
  309. .width = static_cast<u32>(width),
  310. .height = static_cast<u32>(height),
  311. .depth = 1,
  312. },
  313. .selection = {
  314. .width = static_cast<u32>(width),
  315. .height = static_cast<u32>(height),
  316. .depth = 1,
  317. },
  318. };
  319. texture_2d->upload_texture_data(level, internal_format, input_layout, data);
  320. }
  321. void GLContext::gl_tex_parameter(GLenum target, GLenum pname, GLfloat param)
  322. {
  323. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameter, target, pname, param);
  324. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  325. // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
  326. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  327. // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
  328. RETURN_WITH_ERROR_IF(!(pname == GL_TEXTURE_MIN_FILTER
  329. || pname == GL_TEXTURE_MAG_FILTER
  330. || pname == GL_TEXTURE_WRAP_S
  331. || pname == GL_TEXTURE_WRAP_T),
  332. GL_INVALID_ENUM);
  333. // We assume GL_TEXTURE_2D (see above)
  334. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  335. VERIFY(!texture_2d.is_null());
  336. switch (pname) {
  337. case GL_TEXTURE_MIN_FILTER:
  338. RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
  339. || param == GL_LINEAR
  340. || param == GL_NEAREST_MIPMAP_NEAREST
  341. || param == GL_LINEAR_MIPMAP_NEAREST
  342. || param == GL_NEAREST_MIPMAP_LINEAR
  343. || param == GL_LINEAR_MIPMAP_LINEAR),
  344. GL_INVALID_ENUM);
  345. texture_2d->sampler().set_min_filter(param);
  346. break;
  347. case GL_TEXTURE_MAG_FILTER:
  348. RETURN_WITH_ERROR_IF(!(param == GL_NEAREST
  349. || param == GL_LINEAR),
  350. GL_INVALID_ENUM);
  351. texture_2d->sampler().set_mag_filter(param);
  352. break;
  353. case GL_TEXTURE_WRAP_S:
  354. RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
  355. || param == GL_CLAMP_TO_BORDER
  356. || param == GL_CLAMP_TO_EDGE
  357. || param == GL_MIRRORED_REPEAT
  358. || param == GL_REPEAT),
  359. GL_INVALID_ENUM);
  360. texture_2d->sampler().set_wrap_s_mode(param);
  361. break;
  362. case GL_TEXTURE_WRAP_T:
  363. RETURN_WITH_ERROR_IF(!(param == GL_CLAMP
  364. || param == GL_CLAMP_TO_BORDER
  365. || param == GL_CLAMP_TO_EDGE
  366. || param == GL_MIRRORED_REPEAT
  367. || param == GL_REPEAT),
  368. GL_INVALID_ENUM);
  369. texture_2d->sampler().set_wrap_t_mode(param);
  370. break;
  371. default:
  372. VERIFY_NOT_REACHED();
  373. }
  374. m_sampler_config_is_dirty = true;
  375. }
  376. void GLContext::gl_tex_parameterfv(GLenum target, GLenum pname, GLfloat const* params)
  377. {
  378. APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_parameterfv, target, pname, params);
  379. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  380. // FIXME: We currently only support GL_TETXURE_2D targets. 1D, 3D and CUBE should also be supported (https://docs.gl/gl2/glTexParameter)
  381. RETURN_WITH_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM);
  382. // FIXME: implement the remaining parameters. (https://docs.gl/gl2/glTexParameter)
  383. RETURN_WITH_ERROR_IF(!(pname == GL_TEXTURE_BORDER_COLOR), GL_INVALID_ENUM);
  384. // We assume GL_TEXTURE_2D (see above)
  385. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  386. RETURN_WITH_ERROR_IF(texture_2d.is_null(), GL_INVALID_OPERATION);
  387. switch (pname) {
  388. case GL_TEXTURE_BORDER_COLOR:
  389. texture_2d->sampler().set_border_color(params[0], params[1], params[2], params[3]);
  390. break;
  391. default:
  392. VERIFY_NOT_REACHED();
  393. }
  394. m_sampler_config_is_dirty = true;
  395. }
  396. void GLContext::gl_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid const* data)
  397. {
  398. RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
  399. // We only support symbolic constants for now
  400. RETURN_WITH_ERROR_IF(level < 0 || level > Texture2D::LOG2_MAX_TEXTURE_SIZE, GL_INVALID_VALUE);
  401. RETURN_WITH_ERROR_IF(width < 0 || height < 0 || width > (2 + Texture2D::MAX_TEXTURE_SIZE) || height > (2 + Texture2D::MAX_TEXTURE_SIZE), GL_INVALID_VALUE);
  402. // A 2D texture array must have been defined by a previous glTexImage2D operation
  403. auto texture_2d = m_active_texture_unit->texture_2d_target_texture();
  404. VERIFY(!texture_2d.is_null());
  405. RETURN_WITH_ERROR_IF(texture_2d->device_image().is_null(), GL_INVALID_OPERATION);
  406. auto pixel_type_or_error = get_validated_pixel_type(target, texture_2d->internal_format(), format, type);
  407. RETURN_WITH_ERROR_IF(pixel_type_or_error.is_error(), pixel_type_or_error.release_error().code());
  408. RETURN_WITH_ERROR_IF(xoffset < 0 || yoffset < 0 || xoffset + width > texture_2d->width_at_lod(level) || yoffset + height > texture_2d->height_at_lod(level), GL_INVALID_VALUE);
  409. GPU::ImageDataLayout input_layout = {
  410. .pixel_type = pixel_type_or_error.release_value(),
  411. .packing = get_packing_specification(PackingType::Unpack),
  412. .dimensions = {
  413. .width = static_cast<u32>(width),
  414. .height = static_cast<u32>(height),
  415. .depth = 1,
  416. },
  417. .selection = {
  418. .width = static_cast<u32>(width),
  419. .height = static_cast<u32>(height),
  420. .depth = 1,
  421. },
  422. };
  423. texture_2d->replace_sub_texture_data(level, input_layout, { xoffset, yoffset, 0 }, data);
  424. }
  425. void GLContext::sync_device_sampler_config()
  426. {
  427. if (!m_sampler_config_is_dirty)
  428. return;
  429. m_sampler_config_is_dirty = false;
  430. for (unsigned i = 0; i < m_texture_units.size(); ++i) {
  431. auto const& texture_unit = m_texture_units[i];
  432. if (!texture_unit.texture_2d_enabled())
  433. continue;
  434. GPU::SamplerConfig config;
  435. auto texture_2d = texture_unit.texture_2d_target_texture();
  436. VERIFY(!texture_2d.is_null());
  437. config.bound_image = texture_2d->device_image();
  438. auto const& sampler = texture_2d->sampler();
  439. switch (sampler.min_filter()) {
  440. case GL_NEAREST:
  441. config.texture_min_filter = GPU::TextureFilter::Nearest;
  442. config.mipmap_filter = GPU::MipMapFilter::None;
  443. break;
  444. case GL_LINEAR:
  445. config.texture_min_filter = GPU::TextureFilter::Linear;
  446. config.mipmap_filter = GPU::MipMapFilter::None;
  447. break;
  448. case GL_NEAREST_MIPMAP_NEAREST:
  449. config.texture_min_filter = GPU::TextureFilter::Nearest;
  450. config.mipmap_filter = GPU::MipMapFilter::Nearest;
  451. break;
  452. case GL_LINEAR_MIPMAP_NEAREST:
  453. config.texture_min_filter = GPU::TextureFilter::Linear;
  454. config.mipmap_filter = GPU::MipMapFilter::Nearest;
  455. break;
  456. case GL_NEAREST_MIPMAP_LINEAR:
  457. config.texture_min_filter = GPU::TextureFilter::Nearest;
  458. config.mipmap_filter = GPU::MipMapFilter::Linear;
  459. break;
  460. case GL_LINEAR_MIPMAP_LINEAR:
  461. config.texture_min_filter = GPU::TextureFilter::Linear;
  462. config.mipmap_filter = GPU::MipMapFilter::Linear;
  463. break;
  464. default:
  465. VERIFY_NOT_REACHED();
  466. }
  467. switch (sampler.mag_filter()) {
  468. case GL_NEAREST:
  469. config.texture_mag_filter = GPU::TextureFilter::Nearest;
  470. break;
  471. case GL_LINEAR:
  472. config.texture_mag_filter = GPU::TextureFilter::Linear;
  473. break;
  474. default:
  475. VERIFY_NOT_REACHED();
  476. }
  477. switch (sampler.wrap_s_mode()) {
  478. case GL_CLAMP:
  479. config.texture_wrap_u = GPU::TextureWrapMode::Clamp;
  480. break;
  481. case GL_CLAMP_TO_BORDER:
  482. config.texture_wrap_u = GPU::TextureWrapMode::ClampToBorder;
  483. break;
  484. case GL_CLAMP_TO_EDGE:
  485. config.texture_wrap_u = GPU::TextureWrapMode::ClampToEdge;
  486. break;
  487. case GL_REPEAT:
  488. config.texture_wrap_u = GPU::TextureWrapMode::Repeat;
  489. break;
  490. case GL_MIRRORED_REPEAT:
  491. config.texture_wrap_u = GPU::TextureWrapMode::MirroredRepeat;
  492. break;
  493. default:
  494. VERIFY_NOT_REACHED();
  495. }
  496. switch (sampler.wrap_t_mode()) {
  497. case GL_CLAMP:
  498. config.texture_wrap_v = GPU::TextureWrapMode::Clamp;
  499. break;
  500. case GL_CLAMP_TO_BORDER:
  501. config.texture_wrap_v = GPU::TextureWrapMode::ClampToBorder;
  502. break;
  503. case GL_CLAMP_TO_EDGE:
  504. config.texture_wrap_v = GPU::TextureWrapMode::ClampToEdge;
  505. break;
  506. case GL_REPEAT:
  507. config.texture_wrap_v = GPU::TextureWrapMode::Repeat;
  508. break;
  509. case GL_MIRRORED_REPEAT:
  510. config.texture_wrap_v = GPU::TextureWrapMode::MirroredRepeat;
  511. break;
  512. default:
  513. VERIFY_NOT_REACHED();
  514. }
  515. switch (texture_unit.env_mode()) {
  516. case GL_MODULATE:
  517. config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Modulate;
  518. break;
  519. case GL_REPLACE:
  520. config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Replace;
  521. break;
  522. case GL_DECAL:
  523. config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Decal;
  524. break;
  525. case GL_ADD:
  526. config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Add;
  527. break;
  528. default:
  529. VERIFY_NOT_REACHED();
  530. }
  531. config.border_color = sampler.border_color();
  532. m_rasterizer->set_sampler_config(i, config);
  533. }
  534. }
  535. void GLContext::sync_device_texcoord_config()
  536. {
  537. if (!m_texcoord_generation_dirty)
  538. return;
  539. m_texcoord_generation_dirty = false;
  540. auto options = m_rasterizer->options();
  541. for (size_t i = 0; i < m_device_info.num_texture_units; ++i) {
  542. u8 enabled_coordinates = GPU::TexCoordGenerationCoordinate::None;
  543. for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
  544. auto const context_coordinate_config = texture_coordinate_generation(i, capability);
  545. if (!context_coordinate_config.enabled)
  546. continue;
  547. GPU::TexCoordGenerationConfig* texcoord_generation_config;
  548. switch (capability) {
  549. case GL_TEXTURE_GEN_S:
  550. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::S;
  551. texcoord_generation_config = &options.texcoord_generation_config[i][0];
  552. break;
  553. case GL_TEXTURE_GEN_T:
  554. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::T;
  555. texcoord_generation_config = &options.texcoord_generation_config[i][1];
  556. break;
  557. case GL_TEXTURE_GEN_R:
  558. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::R;
  559. texcoord_generation_config = &options.texcoord_generation_config[i][2];
  560. break;
  561. case GL_TEXTURE_GEN_Q:
  562. enabled_coordinates |= GPU::TexCoordGenerationCoordinate::Q;
  563. texcoord_generation_config = &options.texcoord_generation_config[i][3];
  564. break;
  565. default:
  566. VERIFY_NOT_REACHED();
  567. }
  568. switch (context_coordinate_config.generation_mode) {
  569. case GL_OBJECT_LINEAR:
  570. texcoord_generation_config->mode = GPU::TexCoordGenerationMode::ObjectLinear;
  571. texcoord_generation_config->coefficients = context_coordinate_config.object_plane_coefficients;
  572. break;
  573. case GL_EYE_LINEAR:
  574. texcoord_generation_config->mode = GPU::TexCoordGenerationMode::EyeLinear;
  575. texcoord_generation_config->coefficients = context_coordinate_config.eye_plane_coefficients;
  576. break;
  577. case GL_SPHERE_MAP:
  578. texcoord_generation_config->mode = GPU::TexCoordGenerationMode::SphereMap;
  579. break;
  580. case GL_REFLECTION_MAP:
  581. texcoord_generation_config->mode = GPU::TexCoordGenerationMode::ReflectionMap;
  582. break;
  583. case GL_NORMAL_MAP:
  584. texcoord_generation_config->mode = GPU::TexCoordGenerationMode::NormalMap;
  585. break;
  586. }
  587. }
  588. options.texcoord_generation_enabled_coordinates[i] = enabled_coordinates;
  589. }
  590. m_rasterizer->set_options(options);
  591. }
  592. }