Skip to content

Commit 4af99d7

Browse files
avm2: Implement Context3D.setStencilActions and Context3D.setStencilReferenceValue
1 parent e3217e8 commit 4af99d7

File tree

6 files changed

+474
-194
lines changed

6 files changed

+474
-194
lines changed

core/src/avm2/globals/flash/display3D/Context3D.as

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,19 @@ package flash.display3D {
6969
return 2048;
7070
}
7171

72-
public function setStencilReferenceValue(referenceValue:uint, readMask:uint = 255, writeMask:uint = 255):void {
73-
stub_method("flash.display3D.Context3D", "setStencilReferenceValue");
74-
}
72+
public native function setStencilReferenceValue(referenceValue:uint, readMask:uint = 255, writeMask:uint = 255):void;
7573

7674
public native function setSamplerStateAt(sampler:int, wrap:String, filter:String, mipfilter:String):void;
7775

7876
public native function setRenderToTexture(texture:TextureBase, enableDepthAndStencil:Boolean = false, antiAlias:int = 0, surfaceSelector:int = 0, colorOutputIndex:int = 0):void;
7977

80-
public function setStencilActions(
78+
public native function setStencilActions(
8179
triangleFace:String = "frontAndBack",
8280
compareMode:String = "always",
8381
actionOnBothPass:String = "keep",
8482
actionOnDepthFail:String = "keep",
8583
actionOnDepthPassStencilFail:String = "keep"
86-
):void {
87-
stub_method("flash.display3D.Context3D", "setStencilActions");
88-
}
84+
):void;
8985

9086
public native function dispose(recreate:Boolean = true):void;
9187
}

core/src/avm2/globals/flash/display3D/context_3d.rs

Lines changed: 118 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ use crate::avm2::TObject as _;
99
use crate::avm2::Value;
1010
use crate::avm2_stub_method;
1111
use ruffle_macros::istr;
12-
use ruffle_render::backend::Context3DWrapMode;
13-
use ruffle_render::backend::{
14-
BufferUsage, Context3DBlendFactor, Context3DCompareMode, Context3DTextureFormat,
15-
Context3DTriangleFace, Context3DVertexBufferFormat, ProgramType,
16-
};
12+
use ruffle_render::backend::{BufferUsage, Context3DTriangleFace};
1713
use ruffle_render::backend::{Context3DProfile, Context3DTextureFilter};
1814
use swf::{Rectangle, Twips};
1915

@@ -166,25 +162,18 @@ pub fn set_vertex_buffer_at<'gc>(
166162

167163
let buffer = if let Some(buffer) = buffer {
168164
// Note - we only check the format string if the buffer is non-null
169-
let format = args.get_string(activation, 3)?;
170-
171-
let format = if &*format == b"float4" {
172-
Context3DVertexBufferFormat::Float4
173-
} else if &*format == b"float3" {
174-
Context3DVertexBufferFormat::Float3
175-
} else if &*format == b"float2" {
176-
Context3DVertexBufferFormat::Float2
177-
} else if &*format == b"float1" {
178-
Context3DVertexBufferFormat::Float1
179-
} else if &*format == b"bytes4" {
180-
Context3DVertexBufferFormat::Bytes4
181-
} else {
182-
return Err(Error::avm_error(argument_error(
165+
let format = args.get_string(activation, 3)?.parse().map_err(|_| {
166+
let argument_error = argument_error(
183167
activation,
184168
"Error #2008: Parameter vertexStreamFormat must be one of the accepted values.",
185169
2008,
186-
)?));
187-
};
170+
);
171+
172+
match argument_error {
173+
Ok(value) => Error::avm_error(value),
174+
Err(err) => err,
175+
}
176+
})?;
188177

189178
Some((buffer.as_vertex_buffer().unwrap(), format))
190179
} else {
@@ -293,18 +282,10 @@ pub fn set_culling<'gc>(
293282
if let Some(context) = this.as_context_3d() {
294283
let culling = args.get_string(activation, 0)?;
295284

296-
let culling = if &*culling == b"none" {
297-
Context3DTriangleFace::None
298-
} else if &*culling == b"back" {
299-
Context3DTriangleFace::Back
300-
} else if &*culling == b"front" {
301-
Context3DTriangleFace::Front
302-
} else if &*culling == b"frontAndBack" {
303-
Context3DTriangleFace::FrontAndBack
304-
} else {
305-
tracing::error!("Unknown culling {:?}", culling);
306-
Context3DTriangleFace::None
307-
};
285+
let culling = culling
286+
.parse()
287+
.inspect_err(|_| tracing::error!("Unknown culling {:?}", culling))
288+
.unwrap_or(Context3DTriangleFace::None);
308289

309290
context.set_culling(culling);
310291
}
@@ -320,14 +301,9 @@ pub fn set_program_constants_from_matrix<'gc>(
320301

321302
if let Some(context) = this.as_context_3d() {
322303
let program_type = args.get_string(activation, 0)?;
323-
324-
let is_vertex = if &*program_type == b"vertex" {
325-
ProgramType::Vertex
326-
} else if &*program_type == b"fragment" {
327-
ProgramType::Fragment
328-
} else {
329-
panic!("Unknown program type {program_type:?}");
330-
};
304+
let program_type = program_type
305+
.parse()
306+
.unwrap_or_else(|_| panic!("Unknown program type {program_type:?}"));
331307

332308
let first_register = args.get_u32(activation, 1)?;
333309

@@ -363,7 +339,7 @@ pub fn set_program_constants_from_matrix<'gc>(
363339
.map(|val| val.as_f64() as f32)
364340
.collect::<Vec<f32>>();
365341

366-
context.set_program_constants_from_matrix(is_vertex, first_register, matrix_raw_data);
342+
context.set_program_constants_from_matrix(program_type, first_register, matrix_raw_data);
367343
}
368344
Ok(Value::Undefined)
369345
}
@@ -377,14 +353,9 @@ pub fn set_program_constants_from_vector<'gc>(
377353

378354
if let Some(context) = this.as_context_3d() {
379355
let program_type = args.get_string(activation, 0)?;
380-
381-
let program_type = if &*program_type == b"vertex" {
382-
ProgramType::Vertex
383-
} else if &*program_type == b"fragment" {
384-
ProgramType::Fragment
385-
} else {
386-
panic!("Unknown program type {program_type:?}");
387-
};
356+
let program_type = program_type
357+
.parse()
358+
.unwrap_or_else(|_| panic!("Unknown program type {program_type:?}"));
388359

389360
let first_register = args.get_u32(activation, 1)?;
390361

@@ -444,11 +415,12 @@ pub fn create_texture<'gc>(
444415
// This is a native method, so all of the arguments have been checked and coerced for us
445416
let width = args.get_i32(activation, 0)? as u32;
446417
let height = args.get_i32(activation, 1)? as u32;
447-
let format = args.get_string_non_null(activation, 2, "textureFormat")?;
418+
let format = args
419+
.get_string_non_null(activation, 2, "textureFormat")?
420+
.parse()
421+
.map_err(|_| make_error_2008(activation, "textureFormat"))?;
448422
let optimize_for_render_to_texture = args.get_bool(3);
449423
let streaming_levels = args.get_i32(activation, 4)? as u32;
450-
let format = Context3DTextureFormat::from_wstr(&format)
451-
.ok_or_else(|| make_error_2008(activation, "textureFormat"))?;
452424

453425
let class = activation.avm2().classes().texture;
454426

@@ -476,10 +448,11 @@ pub fn create_rectangle_texture<'gc>(
476448
// This is a native method, so all of the arguments have been checked and coerced for us
477449
let width = args.get_i32(activation, 0)? as u32;
478450
let height = args.get_i32(activation, 1)? as u32;
479-
let format = args.get_string_non_null(activation, 2, "textureFormat")?;
451+
let format = args
452+
.get_string_non_null(activation, 2, "textureFormat")?
453+
.parse()
454+
.map_err(|_| make_error_2008(activation, "textureFormat"))?;
480455
let optimize_for_render_to_texture = args.get_bool(3);
481-
let format = Context3DTextureFormat::from_wstr(&format)
482-
.ok_or_else(|| make_error_2008(activation, "textureFormat"))?;
483456

484457
let class = activation.avm2().classes().rectangletexture;
485458

@@ -506,11 +479,12 @@ pub fn create_cube_texture<'gc>(
506479
if let Some(context) = this.as_context_3d() {
507480
// This is a native method, so all of the arguments have been checked and coerced for us
508481
let size = args.get_i32(activation, 0)? as u32;
509-
let format = args.get_string_non_null(activation, 1, "textureFormat")?;
482+
let format = args
483+
.get_string_non_null(activation, 1, "textureFormat")?
484+
.parse()
485+
.map_err(|_| make_error_2008(activation, "textureFormat"))?;
510486
let optimize_for_render_to_texture = args.get_bool(2);
511487
let streaming_levels = args.get_i32(activation, 3)? as u32;
512-
let format = Context3DTextureFormat::from_wstr(&format)
513-
.ok_or_else(|| make_error_2008(activation, "textureFormat"))?;
514488

515489
return context.create_cube_texture(
516490
size,
@@ -582,9 +556,10 @@ pub fn set_depth_test<'gc>(
582556
if let Some(context) = this.as_context_3d() {
583557
// This is a native method, so all of the arguments have been checked and coerced for us
584558
let depth_mask = args.get_bool(0);
585-
let pass_compare_mode = args.get_string_non_null(activation, 1, "passCompareMode")?;
586-
let pass_compare_mode = Context3DCompareMode::from_wstr(&pass_compare_mode)
587-
.ok_or_else(|| make_error_2008(activation, "passCompareMode"))?;
559+
let pass_compare_mode = args
560+
.get_string_non_null(activation, 1, "passCompareMode")?
561+
.parse()
562+
.map_err(|_| make_error_2008(activation, "passCompareMode"))?;
588563

589564
context.set_depth_test(depth_mask, pass_compare_mode);
590565
}
@@ -600,14 +575,14 @@ pub fn set_blend_factors<'gc>(
600575

601576
if let Some(context) = this.as_context_3d() {
602577
// This is a native method, so all of the arguments have been checked and coerced for us
603-
let source_factor = args.get_string_non_null(activation, 0, "sourceFactor")?;
604-
let destination_factor = args.get_string_non_null(activation, 1, "destinationFactor")?;
605-
606-
let source_factor = Context3DBlendFactor::from_wstr(&source_factor)
607-
.ok_or_else(|| make_error_2008(activation, "sourceFactor"))?;
608-
609-
let destination_factor = Context3DBlendFactor::from_wstr(&destination_factor)
610-
.ok_or_else(|| make_error_2008(activation, "destinationFactor"))?;
578+
let source_factor = args
579+
.get_string_non_null(activation, 0, "sourceFactor")?
580+
.parse()
581+
.map_err(|_| make_error_2008(activation, "sourceFactor"))?;
582+
let destination_factor = args
583+
.get_string_non_null(activation, 1, "destinationFactor")?
584+
.parse()
585+
.map_err(|_| make_error_2008(activation, "destinationFactor"))?;
611586

612587
context.set_blend_factors(source_factor, destination_factor);
613588
}
@@ -686,6 +661,53 @@ pub fn set_render_to_texture<'gc>(
686661
Ok(Value::Undefined)
687662
}
688663

664+
pub fn set_stencil_actions<'gc>(
665+
activation: &mut Activation<'_, 'gc>,
666+
this: Value<'gc>,
667+
args: &[Value<'gc>],
668+
) -> Result<Value<'gc>, Error<'gc>> {
669+
let this = this.as_object().unwrap();
670+
671+
if let Some(context) = this.as_context_3d() {
672+
let triangle_face = args.get_string(activation, 0)?;
673+
674+
let triangle_face = triangle_face
675+
.parse()
676+
.inspect_err(|_| tracing::error!("Unknown triangle_face {:?}", triangle_face))
677+
.unwrap_or(Context3DTriangleFace::None);
678+
679+
let compare_mode = args
680+
.get_string_non_null(activation, 1, "compareMode")?
681+
.parse()
682+
.map_err(|_| make_error_2008(activation, "compareMode"))?;
683+
684+
let on_both_pass = args
685+
.get_string_non_null(activation, 2, "actionOnBothPass")?
686+
.parse()
687+
.map_err(|_| make_error_2008(activation, "actionOnBothPass"))?;
688+
689+
let on_depth_fail = args
690+
.get_string_non_null(activation, 3, "actionOnDepthFail")?
691+
.parse()
692+
.map_err(|_| make_error_2008(activation, "actionOnDepthFail"))?;
693+
694+
let on_depth_pass_stencil_fail = args
695+
.get_string_non_null(activation, 4, "actionOnDepthPassStencilFail")?
696+
.parse()
697+
.map_err(|_| make_error_2008(activation, "actionOnDepthPassStencilFail"))?;
698+
699+
context.set_stencil_actions(
700+
triangle_face,
701+
compare_mode,
702+
on_both_pass,
703+
on_depth_fail,
704+
on_depth_pass_stencil_fail,
705+
);
706+
}
707+
708+
Ok(Value::Undefined)
709+
}
710+
689711
pub fn set_render_to_back_buffer<'gc>(
690712
_activation: &mut Activation<'_, 'gc>,
691713
this: Value<'gc>,
@@ -698,6 +720,24 @@ pub fn set_render_to_back_buffer<'gc>(
698720
Ok(Value::Undefined)
699721
}
700722

723+
pub fn set_stencil_reference_value<'gc>(
724+
activation: &mut Activation<'_, 'gc>,
725+
this: Value<'gc>,
726+
args: &[Value<'gc>],
727+
) -> Result<Value<'gc>, Error<'gc>> {
728+
let this = this.as_object().unwrap();
729+
730+
if let Some(context) = this.as_context_3d() {
731+
let reference_value = args.get_u32(activation, 0)?;
732+
let read_mask = args.get_u32(activation, 1)?;
733+
let write_mask = args.get_u32(activation, 2)?;
734+
735+
context.set_stencil_reference_value(reference_value, read_mask, write_mask);
736+
}
737+
738+
Ok(Value::Undefined)
739+
}
740+
701741
pub fn set_sampler_state_at<'gc>(
702742
activation: &mut Activation<'_, 'gc>,
703743
this: Value<'gc>,
@@ -708,16 +748,16 @@ pub fn set_sampler_state_at<'gc>(
708748
if let Some(context) = this.as_context_3d() {
709749
// This is a native method, so all of the arguments have been checked and coerced for us
710750
let sampler = args[0].as_i32() as u32;
711-
let wrap = args[1].coerce_to_string(activation)?;
712-
let filter = args[2].coerce_to_string(activation)?;
751+
let wrap = args[1]
752+
.coerce_to_string(activation)?
753+
.parse()
754+
.map_err(|_| make_error_2008(activation, "wrap"))?;
755+
let filter = args[2]
756+
.coerce_to_string(activation)?
757+
.parse()
758+
.map_err(|_| make_error_2008(activation, "filter"))?;
713759
let mip_filter = args[3].coerce_to_string(activation)?;
714760

715-
let wrap = Context3DWrapMode::from_wstr(&wrap)
716-
.ok_or_else(|| make_error_2008(activation, "wrap"))?;
717-
718-
let filter = Context3DTextureFilter::from_wstr(&filter)
719-
.ok_or_else(|| make_error_2008(activation, "filter"))?;
720-
721761
if matches!(
722762
filter,
723763
Context3DTextureFilter::Anisotropic2X

core/src/avm2/object/context3d_object.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::utils::HasPrefixField;
1212
use gc_arena::{Collect, Gc, GcCell, GcWeak};
1313
use ruffle_render::backend::{
1414
BufferUsage, Context3D, Context3DBlendFactor, Context3DCommand, Context3DCompareMode,
15-
Context3DTextureFormat, Context3DTriangleFace, Context3DVertexBufferFormat, ProgramType,
16-
Texture,
15+
Context3DStencilAction, Context3DTextureFormat, Context3DTriangleFace,
16+
Context3DVertexBufferFormat, ProgramType, Texture,
1717
};
1818
use ruffle_render::commands::CommandHandler;
1919
use std::cell::Cell;
@@ -476,6 +476,40 @@ impl<'gc> Context3DObject<'gc> {
476476
ctx.process_command(Context3DCommand::SetScissorRectangle { rect })
477477
});
478478
}
479+
480+
pub(crate) fn set_stencil_actions(
481+
&self,
482+
triangle_face: Context3DTriangleFace,
483+
compare_mode: Context3DCompareMode,
484+
on_both_pass: Context3DStencilAction,
485+
on_depth_fail: Context3DStencilAction,
486+
on_depth_pass_stencil_fail: Context3DStencilAction,
487+
) {
488+
self.with_context_3d(|ctx| {
489+
ctx.process_command(Context3DCommand::SetStencilActions {
490+
triangle_face,
491+
compare_mode,
492+
on_both_pass,
493+
on_depth_fail,
494+
on_depth_pass_stencil_fail,
495+
})
496+
});
497+
}
498+
499+
pub(crate) fn set_stencil_reference_value(
500+
&self,
501+
reference_value: u32,
502+
read_mask: u32,
503+
write_mask: u32,
504+
) {
505+
self.with_context_3d(|ctx| {
506+
ctx.process_command(Context3DCommand::SetStencilReferenceValue {
507+
reference_value,
508+
read_mask,
509+
write_mask,
510+
})
511+
});
512+
}
479513
}
480514

481515
#[derive(Collect, HasPrefixField)]

0 commit comments

Comments
 (0)